0
# Workspace Server
1
2
The workspace server provides backend workspace management functionality including workspace persistence, recent workspace tracking, and server-side workspace operations. It operates over JSON-RPC and handles workspace state across application sessions.
3
4
## Capabilities
5
6
### Core Workspace Server Protocol
7
8
The main server interface for workspace operations exposed over JSON-RPC.
9
10
```typescript { .api }
11
/**
12
* JSON-RPC endpoint path for workspace service
13
*/
14
const workspacePath = '/services/workspace';
15
16
/**
17
* Workspace server interface for backend workspace operations
18
*/
19
interface WorkspaceServer {
20
/**
21
* Returns the most recently used workspace folder URI as a string
22
*/
23
getMostRecentlyUsedWorkspace(): Promise<string | undefined>;
24
25
/**
26
* Sets the desired URI as the most recently used workspace folder
27
* @param uri - String representation of the workspace URI
28
*/
29
setMostRecentlyUsedWorkspace(uri: string): Promise<void>;
30
31
/**
32
* Removes a workspace from the list of recently opened workspaces
33
* @param uri - The workspace URI to remove
34
*/
35
removeRecentWorkspace(uri: string): Promise<void>;
36
37
/**
38
* Returns list of recently opened workspaces as an array
39
*/
40
getRecentWorkspaces(): Promise<string[]>;
41
}
42
```
43
44
**Usage Example:**
45
46
```typescript
47
import { injectable, inject } from "@theia/core/shared/inversify";
48
import { WorkspaceServer } from "@theia/workspace/lib/common";
49
50
@injectable()
51
export class MyWorkspaceClient {
52
53
@inject(WorkspaceServer)
54
protected readonly workspaceServer: WorkspaceServer;
55
56
async displayRecentWorkspaces(): Promise<void> {
57
// Get recent workspaces
58
const recentWorkspaces = await this.workspaceServer.getRecentWorkspaces();
59
console.log("Recent workspaces:", recentWorkspaces);
60
61
// Get most recently used
62
const mostRecent = await this.workspaceServer.getMostRecentlyUsedWorkspace();
63
if (mostRecent) {
64
console.log("Most recent workspace:", mostRecent);
65
}
66
}
67
68
async setCurrentWorkspace(workspaceUri: string): Promise<void> {
69
// Set as most recently used
70
await this.workspaceServer.setMostRecentlyUsedWorkspace(workspaceUri);
71
console.log(`Set ${workspaceUri} as most recent workspace`);
72
}
73
74
async cleanupOldWorkspace(workspaceUri: string): Promise<void> {
75
// Remove from recent list
76
await this.workspaceServer.removeRecentWorkspace(workspaceUri);
77
console.log(`Removed ${workspaceUri} from recent workspaces`);
78
}
79
}
80
```
81
82
### Default Workspace Server Implementation
83
84
Backend implementation that handles persistence and workspace lifecycle management.
85
86
```typescript { .api }
87
class DefaultWorkspaceServer implements WorkspaceServer, BackendApplicationContribution {
88
/**
89
* Threshold for untitled workspace cleanup (days)
90
*/
91
protected readonly untitledWorkspaceStaleThreshold: number;
92
93
/**
94
* Backend application lifecycle - called on server start
95
*/
96
onStart(): void;
97
98
/**
99
* Get most recently used workspace (implements WorkspaceServer)
100
*/
101
getMostRecentlyUsedWorkspace(): Promise<string | undefined>;
102
103
/**
104
* Set most recently used workspace and persist to storage
105
*/
106
setMostRecentlyUsedWorkspace(rawUri: string): Promise<void>;
107
108
/**
109
* Remove workspace from recent list and update storage
110
*/
111
removeRecentWorkspace(rawUri: string): Promise<void>;
112
113
/**
114
* Get filtered list of recent workspaces that still exist
115
*/
116
getRecentWorkspaces(): Promise<string[]>;
117
}
118
```
119
120
**Usage Example:**
121
122
```typescript
123
import { injectable, inject } from "@theia/core/shared/inversify";
124
import { DefaultWorkspaceServer } from "@theia/workspace/lib/node";
125
126
@injectable()
127
export class MyWorkspaceManager {
128
129
@inject(DefaultWorkspaceServer)
130
protected readonly workspaceServer: DefaultWorkspaceServer;
131
132
async initializeWorkspace(): Promise<void> {
133
// The server automatically handles:
134
// - Loading recent workspaces from ~/.config/theia/recentworkspace.json
135
// - Validating that workspaces still exist
136
// - Cleaning up old untitled workspaces (older than 10 days by default)
137
// - Processing CLI workspace arguments
138
139
const mostRecent = await this.workspaceServer.getMostRecentlyUsedWorkspace();
140
if (mostRecent) {
141
console.log(`Restoring workspace: ${mostRecent}`);
142
}
143
}
144
}
145
```
146
147
### CLI Integration
148
149
Service for handling workspace arguments from the command line.
150
151
```typescript { .api }
152
class WorkspaceCliContribution implements CliContribution {
153
/**
154
* Deferred workspace root from CLI arguments
155
*/
156
readonly workspaceRoot: Deferred<string | undefined>;
157
158
/**
159
* Configure CLI argument parsing for workspace options
160
*/
161
configure(conf: yargs.Argv): void;
162
163
/**
164
* Process parsed CLI arguments and set workspace
165
*/
166
setArguments(args: yargs.Arguments): Promise<void>;
167
168
/**
169
* Normalize workspace argument to proper format
170
*/
171
protected normalizeWorkspaceArg(raw: string): string;
172
173
/**
174
* Build untitled workspace for multiple directory arguments
175
*/
176
protected buildWorkspaceForMultipleArguments(workspaceArguments: string[]): Promise<string | undefined>;
177
}
178
```
179
180
**Usage Example:**
181
182
```typescript
183
import { injectable, inject } from "@theia/core/shared/inversify";
184
import { WorkspaceCliContribution } from "@theia/workspace/lib/node";
185
186
@injectable()
187
export class MyApplicationContribution {
188
189
@inject(WorkspaceCliContribution)
190
protected readonly cliContribution: WorkspaceCliContribution;
191
192
async handleStartup(): Promise<void> {
193
// Wait for CLI processing to complete
194
const workspaceFromCli = await this.cliContribution.workspaceRoot.promise;
195
196
if (workspaceFromCli) {
197
console.log(`Opening workspace from CLI: ${workspaceFromCli}`);
198
// The CLI contribution handles:
199
// - Single directory arguments
200
// - Multiple directory arguments (creates untitled workspace)
201
// - Workspace file arguments
202
// - Legacy --root-dir flag
203
}
204
}
205
}
206
207
// CLI usage examples:
208
// theia /path/to/workspace
209
// theia /path/to/project1 /path/to/project2 # Creates untitled workspace
210
// theia workspace.theia-workspace
211
// theia --root-dir=/legacy/path # Deprecated but supported
212
```
213
214
### Extension Points
215
216
Interfaces for extending workspace server functionality with custom handlers.
217
218
```typescript { .api }
219
/**
220
* Extension point for custom workspace handlers
221
*/
222
interface WorkspaceHandlerContribution {
223
/**
224
* Check if this handler can handle the given URI scheme/format
225
*/
226
canHandle(uri: URI): boolean;
227
228
/**
229
* Check if the workspace still exists and is accessible
230
*/
231
workspaceStillExists(uri: URI): Promise<boolean>;
232
}
233
234
/**
235
* Default file system workspace handler
236
*/
237
class FileWorkspaceHandlerContribution implements WorkspaceHandlerContribution {
238
/**
239
* Handles file:// scheme URIs
240
*/
241
canHandle(uri: URI): boolean;
242
243
/**
244
* Check if file/directory exists on disk
245
*/
246
workspaceStillExists(uri: URI): Promise<boolean>;
247
}
248
```
249
250
**Usage Example:**
251
252
```typescript
253
import { injectable } from "@theia/core/shared/inversify";
254
import { WorkspaceHandlerContribution } from "@theia/workspace/lib/node";
255
import URI from "@theia/core/lib/common/uri";
256
257
@injectable()
258
export class RemoteWorkspaceHandler implements WorkspaceHandlerContribution {
259
260
canHandle(uri: URI): boolean {
261
// Handle custom remote workspace schemes
262
return uri.scheme === 'ssh' || uri.scheme === 'ftp';
263
}
264
265
async workspaceStillExists(uri: URI): Promise<boolean> {
266
// Custom logic to check remote workspace availability
267
if (uri.scheme === 'ssh') {
268
return this.checkSshWorkspace(uri);
269
} else if (uri.scheme === 'ftp') {
270
return this.checkFtpWorkspace(uri);
271
}
272
return false;
273
}
274
275
private async checkSshWorkspace(uri: URI): Promise<boolean> {
276
// Implementation for SSH workspace validation
277
try {
278
// SSH connection logic here
279
return true;
280
} catch {
281
return false;
282
}
283
}
284
285
private async checkFtpWorkspace(uri: URI): Promise<boolean> {
286
// Implementation for FTP workspace validation
287
try {
288
// FTP connection logic here
289
return true;
290
} catch {
291
return false;
292
}
293
}
294
}
295
296
// Register the custom handler in your module:
297
// bind(WorkspaceHandlerContribution).to(RemoteWorkspaceHandler).inSingletonScope();
298
```
299
300
### Recent Workspace Data Management
301
302
Data structures and utilities for managing workspace persistence.
303
304
```typescript { .api }
305
interface RecentWorkspacePathsData {
306
/** Array of recent workspace URI strings */
307
recentRoots: string[];
308
}
309
310
namespace RecentWorkspacePathsData {
311
/**
312
* Create RecentWorkspacePathsData from unknown data with validation
313
*/
314
function create(data: unknown): RecentWorkspacePathsData | undefined;
315
}
316
```
317
318
**Usage Example:**
319
320
```typescript
321
import { RecentWorkspacePathsData } from "@theia/workspace/lib/node";
322
import * as fs from "fs-extra";
323
324
export class WorkspaceStorageManager {
325
326
private readonly recentWorkspacesPath = "~/.config/theia/recentworkspace.json";
327
328
async loadRecentWorkspaces(): Promise<string[]> {
329
try {
330
const data = await fs.readJson(this.recentWorkspacesPath);
331
const workspaceData = RecentWorkspacePathsData.create(data);
332
return workspaceData?.recentRoots || [];
333
} catch {
334
return [];
335
}
336
}
337
338
async saveRecentWorkspaces(workspaces: string[]): Promise<void> {
339
const data: RecentWorkspacePathsData = {
340
recentRoots: workspaces
341
};
342
await fs.ensureDir(path.dirname(this.recentWorkspacesPath));
343
await fs.writeJson(this.recentWorkspacesPath, data, { spaces: 2 });
344
}
345
346
async addRecentWorkspace(workspace: string): Promise<void> {
347
const recent = await this.loadRecentWorkspaces();
348
349
// Remove if already exists (to move to front)
350
const filtered = recent.filter(w => w !== workspace);
351
352
// Add to front and limit to reasonable number
353
const updated = [workspace, ...filtered].slice(0, 10);
354
355
await this.saveRecentWorkspaces(updated);
356
}
357
}
358
```
359
360
### Server Configuration and Lifecycle
361
362
The workspace server integrates with Theia's backend application lifecycle and provides automatic cleanup and maintenance.
363
364
**Key Features:**
365
366
- **Automatic Cleanup**: Removes untitled workspaces older than configurable threshold (default: 10 days)
367
- **Persistence**: Stores recent workspaces in user config directory
368
- **Validation**: Checks workspace existence before returning in recent list
369
- **CLI Integration**: Processes command-line workspace arguments
370
- **Extension Support**: Pluggable workspace handlers for different URI schemes
371
372
## Types
373
374
```typescript { .api }
375
interface RecentWorkspacePathsData {
376
recentRoots: string[];
377
}
378
379
interface WorkspaceHandlerContribution {
380
canHandle(uri: URI): boolean;
381
workspaceStillExists(uri: URI): Promise<boolean>;
382
}
383
384
interface CliContribution {
385
configure(conf: yargs.Argv): void;
386
setArguments(args: yargs.Arguments): Promise<void>;
387
}
388
389
interface BackendApplicationContribution {
390
initialize?(): void;
391
configure?(app: Application): void;
392
onStart?(server?: http.Server): void;
393
onStop?(app?: Application): void;
394
}
395
396
const workspacePath: string; // '/services/workspace'
397
const WorkspaceServer: symbol;
398
const WorkspaceHandlerContribution: symbol;
399
400
type Deferred<T> = {
401
readonly promise: Promise<T>;
402
resolve(value: T): void;
403
reject(reason?: any): void;
404
};
405
```