0
# Workspace Service
1
2
The WorkspaceService is the core service for workspace lifecycle management, providing workspace opening/closing, root folder management, and workspace state tracking. It serves as the central entry point for all workspace-related operations in Theia applications.
3
4
## Capabilities
5
6
### Service Initialization
7
8
Access to the service's ready state and workspace data.
9
10
```typescript { .api }
11
/**
12
* Promise that resolves when the service is ready to use
13
*/
14
readonly ready: Promise<void>;
15
16
/**
17
* Promise that resolves to the workspace root folders
18
*/
19
readonly roots: Promise<FileStat[]>;
20
21
/**
22
* Get synchronous access to current roots (may be empty if not ready)
23
*/
24
tryGetRoots(): FileStat[];
25
```
26
27
**Usage Example:**
28
29
```typescript
30
import { injectable, inject } from "@theia/core/shared/inversify";
31
import { WorkspaceService } from "@theia/workspace/lib/browser";
32
33
@injectable()
34
export class MyService {
35
36
@inject(WorkspaceService)
37
protected readonly workspaceService: WorkspaceService;
38
39
async initialize(): Promise<void> {
40
// Wait for service to be ready
41
await this.workspaceService.ready;
42
43
// Get workspace roots
44
const roots = await this.workspaceService.roots;
45
console.log(`Workspace has ${roots.length} root folders`);
46
47
// Or get roots synchronously (may be empty if not ready)
48
const syncRoots = this.workspaceService.tryGetRoots();
49
}
50
}
51
```
52
53
### Workspace State Management
54
55
Access to current workspace state and properties.
56
57
```typescript { .api }
58
/**
59
* Current workspace file or directory (synchronous access)
60
*/
61
readonly workspace: FileStat | undefined;
62
63
/**
64
* Returns true if theia has an opened workspace or folder
65
*/
66
readonly opened: boolean;
67
68
/**
69
* Returns true if a multiple-root workspace is currently open
70
*/
71
readonly isMultiRootWorkspaceOpened: boolean;
72
73
/**
74
* True if the current workspace is configured using a configuration file.
75
* False if there is no workspace or the workspace is simply a folder.
76
*/
77
readonly saved: boolean;
78
```
79
80
**Usage Example:**
81
82
```typescript
83
// Check workspace state
84
if (this.workspaceService.opened) {
85
if (this.workspaceService.isMultiRootWorkspaceOpened) {
86
console.log("Multi-root workspace is open");
87
} else {
88
console.log("Single folder workspace is open");
89
}
90
91
if (this.workspaceService.saved) {
92
console.log("Workspace is saved as a .theia-workspace file");
93
console.log(`Workspace file: ${this.workspaceService.workspace?.uri}`);
94
}
95
} else {
96
console.log("No workspace is currently open");
97
}
98
```
99
100
### Workspace Opening and Closing
101
102
Core workspace lifecycle operations.
103
104
```typescript { .api }
105
/**
106
* Opens directory, or recreates a workspace from the file that `uri` points to.
107
* @param uri - URI of the directory or workspace file to open
108
* @param options - Options for opening the workspace
109
*/
110
open(uri: URI, options?: WorkspaceInput): void;
111
112
/**
113
* Clears current workspace root.
114
*/
115
close(): Promise<void>;
116
117
/**
118
* Save workspace data into a file
119
* @param uri - URI or FileStat of the workspace file
120
*/
121
save(uri: URI | FileStat): Promise<void>;
122
```
123
124
**Usage Example:**
125
126
```typescript
127
import URI from "@theia/core/lib/common/uri";
128
129
// Open a folder as workspace
130
const folderUri = new URI("file:///path/to/project");
131
this.workspaceService.open(folderUri);
132
133
// Open a workspace file
134
const workspaceUri = new URI("file:///path/to/project.theia-workspace");
135
this.workspaceService.open(workspaceUri, { preserveWindow: true });
136
137
// Save current workspace to a file
138
const saveUri = new URI("file:///path/to/my-workspace.theia-workspace");
139
await this.workspaceService.save(saveUri);
140
141
// Close current workspace
142
await this.workspaceService.close();
143
```
144
145
### Root Folder Management
146
147
Managing workspace root folders for multi-root workspaces.
148
149
```typescript { .api }
150
/**
151
* Adds root folder(s) to the workspace
152
* @param uris - URI or URIs of the root folder(s) to add
153
*/
154
addRoot(uris: URI[] | URI): Promise<void>;
155
156
/**
157
* Removes root folder(s) from workspace.
158
* @param uris - URIs of the root folders to remove
159
*/
160
removeRoots(uris: URI[]): Promise<void>;
161
162
/**
163
* Splice roots - remove and/or add roots at specific position
164
* @param start - Starting index for splice operation
165
* @param deleteCount - Number of roots to remove (optional)
166
* @param rootsToAdd - URIs of roots to add at the start position
167
*/
168
spliceRoots(start: number, deleteCount?: number, ...rootsToAdd: URI[]): Promise<URI[]>;
169
```
170
171
**Usage Example:**
172
173
```typescript
174
// Add a single root folder
175
const newRoot = new URI("file:///path/to/additional-project");
176
await this.workspaceService.addRoot(newRoot);
177
178
// Add multiple root folders
179
const roots = [
180
new URI("file:///path/to/project-a"),
181
new URI("file:///path/to/project-b")
182
];
183
await this.workspaceService.addRoot(roots);
184
185
// Remove specific roots
186
const rootsToRemove = [new URI("file:///path/to/old-project")];
187
await this.workspaceService.removeRoots(rootsToRemove);
188
189
// Replace the first root with a new one
190
const newRoots = await this.workspaceService.spliceRoots(
191
0, 1, new URI("file:///path/to/replacement-project")
192
);
193
```
194
195
### Workspace Utilities
196
197
Utility methods for workspace path operations and validation.
198
199
```typescript { .api }
200
/**
201
* Return true if one of the paths in paths array is present in the workspace
202
* NOTE: You should always explicitly use `/` as the separator between the path segments.
203
* @param paths - Array of paths to check
204
*/
205
containsSome(paths: string[]): Promise<boolean>;
206
207
/**
208
* Returns the workspace root uri that the given file belongs to.
209
* In case that the file is found in more than one workspace roots, returns the root that is closest to the file.
210
* If the file is not from the current workspace, returns `undefined`.
211
* @param uri - URI of the file
212
*/
213
getWorkspaceRootUri(uri: URI | undefined): URI | undefined;
214
215
/**
216
* Returns the relative path of the given file to the workspace root.
217
* @param uri - URI of the file
218
*/
219
getWorkspaceRelativePath(uri: URI): Promise<string>;
220
221
/**
222
* Check if the given URIs are workspace roots
223
* @param uris - URIs to check
224
*/
225
areWorkspaceRoots(uris: URI[]): boolean;
226
227
/**
228
* Check if the given URI is an untitled workspace
229
* @param candidate - URI to check (optional, defaults to current workspace)
230
*/
231
isUntitledWorkspace(candidate?: URI): boolean;
232
233
/**
234
* Check if it's safe to reload with the given URI
235
* @param withURI - URI to check for reload safety
236
*/
237
isSafeToReload(withURI?: URI): Promise<boolean>;
238
```
239
240
**Usage Example:**
241
242
```typescript
243
// Check if workspace contains specific paths
244
const hasPackageJson = await this.workspaceService.containsSome(['package.json']);
245
const hasSourceFiles = await this.workspaceService.containsSome(['src/main.ts', 'lib/index.js']);
246
247
// Get workspace root for a file
248
const fileUri = new URI("file:///workspace/src/components/button.tsx");
249
const rootUri = this.workspaceService.getWorkspaceRootUri(fileUri);
250
if (rootUri) {
251
console.log(`File belongs to workspace root: ${rootUri}`);
252
}
253
254
// Get relative path within workspace
255
const relativePath = await this.workspaceService.getWorkspaceRelativePath(fileUri);
256
console.log(`Relative path: ${relativePath}`); // e.g., "src/components/button.tsx"
257
258
// Check if URIs are workspace roots
259
const someUris = [new URI("file:///project-a"), new URI("file:///project-b")];
260
if (this.workspaceService.areWorkspaceRoots(someUris)) {
261
console.log("These URIs are workspace roots");
262
}
263
264
// Check if workspace is untitled
265
if (this.workspaceService.isUntitledWorkspace()) {
266
console.log("Current workspace is untitled");
267
}
268
```
269
270
### Recent Workspaces Management
271
272
Managing the list of recently used workspaces.
273
274
```typescript { .api }
275
/**
276
* Get recent workspaces list
277
*/
278
recentWorkspaces(): Promise<string[]>;
279
280
/**
281
* Remove a workspace from recent workspaces list
282
* @param uri - URI string of the workspace to remove
283
*/
284
removeRecentWorkspace(uri: string): Promise<void>;
285
```
286
287
**Usage Example:**
288
289
```typescript
290
// Get list of recent workspaces
291
const recentWorkspaces = await this.workspaceService.recentWorkspaces();
292
console.log("Recent workspaces:", recentWorkspaces);
293
294
// Remove a workspace from recent list
295
await this.workspaceService.removeRecentWorkspace("file:///old/workspace/path");
296
```
297
298
### Event Handling
299
300
Events fired when workspace state changes.
301
302
```typescript { .api }
303
/**
304
* Event fired when workspace roots change
305
*/
306
readonly onWorkspaceChanged: Event<FileStat[]>;
307
308
/**
309
* Event fired when workspace location changes (e.g., when saving untitled workspace)
310
*/
311
readonly onWorkspaceLocationChanged: Event<FileStat | undefined>;
312
```
313
314
**Usage Example:**
315
316
```typescript
317
import { Disposable } from "@theia/core";
318
319
// Listen to workspace changes
320
const disposable1: Disposable = this.workspaceService.onWorkspaceChanged(roots => {
321
console.log(`Workspace roots changed. New count: ${roots.length}`);
322
roots.forEach((root, index) => {
323
console.log(`Root ${index}: ${root.uri}`);
324
});
325
});
326
327
// Listen to workspace location changes
328
const disposable2: Disposable = this.workspaceService.onWorkspaceLocationChanged(workspace => {
329
if (workspace) {
330
console.log(`Workspace location changed to: ${workspace.uri}`);
331
} else {
332
console.log("Workspace was closed");
333
}
334
});
335
336
// Don't forget to dispose listeners when done
337
disposable1.dispose();
338
disposable2.dispose();
339
```
340
341
### Schema Management
342
343
Managing workspace configuration schemas.
344
345
```typescript { .api }
346
/**
347
* Update workspace schema
348
* @param key - The property key under which to store the schema (e.g. tasks, launch)
349
* @param schema - The schema for the property. If none is supplied, the update is treated as a deletion.
350
*/
351
updateSchema(key: string, schema?: IJSONSchema): Promise<boolean>;
352
```
353
354
**Usage Example:**
355
356
```typescript
357
import { IJSONSchema } from "@theia/core/lib/common/json-schema";
358
359
// Add a custom schema for workspace configuration
360
const customSchema: IJSONSchema = {
361
type: "object",
362
properties: {
363
customTool: {
364
type: "object",
365
properties: {
366
enabled: { type: "boolean" },
367
settings: { type: "object" }
368
}
369
}
370
}
371
};
372
373
const success = await this.workspaceService.updateSchema("customTool", customSchema);
374
if (success) {
375
console.log("Schema updated successfully");
376
}
377
378
// Remove a schema by passing undefined
379
await this.workspaceService.updateSchema("customTool", undefined);
380
```
381
382
## Types
383
384
```typescript { .api }
385
interface WorkspaceInput {
386
/**
387
* Tests whether the same window should be used or a new one has to be opened after setting the workspace root.
388
* By default it is `false`.
389
*/
390
preserveWindow?: boolean;
391
}
392
393
interface WorkspaceData {
394
folders: Array<{ path: string; name?: string }>;
395
[key: string]: { [id: string]: any };
396
}
397
398
namespace WorkspaceData {
399
/**
400
* Type guard to check if data is WorkspaceData
401
*/
402
function is(data: unknown): data is WorkspaceData;
403
404
/**
405
* Build workspace data from folders and additional fields
406
*/
407
function buildWorkspaceData(folders: string[] | FileStat[], additionalFields?: Partial<WorkspaceData>): WorkspaceData;
408
409
/**
410
* Transform workspace data to relative paths
411
*/
412
function transformToRelative(data: WorkspaceData, workspaceFile?: FileStat): WorkspaceData;
413
414
/**
415
* Transform workspace data to absolute paths
416
*/
417
function transformToAbsolute(data: WorkspaceData, workspaceFile?: BaseStat): WorkspaceData;
418
}
419
```