0
# Workspace File Handling
1
2
The workspace file handling module provides services for managing workspace file formats, validation, and untitled workspace creation. It supports both Theia and Visual Studio Code workspace file formats and handles the lifecycle of temporary workspace files.
3
4
## Capabilities
5
6
### Workspace File Type Management
7
8
Service for managing supported workspace file formats and validation.
9
10
```typescript { .api }
11
interface WorkspaceFileType {
12
/** File extension without dot (e.g., 'theia-workspace') */
13
extension: string;
14
/** Display name for the file type (e.g., 'Theia') */
15
name: string;
16
}
17
18
class WorkspaceFileService {
19
/**
20
* Index of the default workspace file type (currently 0 for Theia format)
21
*/
22
readonly defaultFileTypeIndex: number;
23
24
/**
25
* Check if the file should be considered as a workspace file
26
* @param candidate - FileStat object or URI to check
27
*/
28
isWorkspaceFile(candidate: FileStat | URI): boolean;
29
30
/**
31
* Get all supported workspace file types
32
*/
33
getWorkspaceFileTypes(): WorkspaceFileType[];
34
35
/**
36
* Get workspace file extensions, optionally with dot prefix
37
* @param dot - Whether to include dot prefix (e.g., '.theia-workspace')
38
*/
39
getWorkspaceFileExtensions(dot?: boolean): string[];
40
}
41
```
42
43
**Usage Example:**
44
45
```typescript
46
import { injectable, inject } from "@theia/core/shared/inversify";
47
import { WorkspaceFileService, WorkspaceFileType } from "@theia/workspace/lib/common";
48
import URI from "@theia/core/lib/common/uri";
49
50
@injectable()
51
export class MyWorkspaceValidator {
52
53
@inject(WorkspaceFileService)
54
protected readonly workspaceFileService: WorkspaceFileService;
55
56
validateWorkspaceFile(uri: URI): boolean {
57
// Check if file is a valid workspace file
58
if (!this.workspaceFileService.isWorkspaceFile(uri)) {
59
console.log(`${uri} is not a valid workspace file`);
60
return false;
61
}
62
return true;
63
}
64
65
getSupportedFormats(): WorkspaceFileType[] {
66
// Get all supported workspace file types
67
const types = this.workspaceFileService.getWorkspaceFileTypes();
68
// Returns: [{ name: 'Theia', extension: 'theia-workspace' },
69
// { name: 'Visual Studio Code', extension: 'code-workspace' }]
70
return types;
71
}
72
73
createFileFilter(): string {
74
// Create file filter for file dialogs
75
const extensions = this.workspaceFileService.getWorkspaceFileExtensions(true);
76
return extensions.join(','); // '.theia-workspace,.code-workspace'
77
}
78
79
getDefaultExtension(): string {
80
const types = this.workspaceFileService.getWorkspaceFileTypes();
81
const defaultIndex = this.workspaceFileService.defaultFileTypeIndex;
82
return types[defaultIndex].extension; // 'theia-workspace'
83
}
84
}
85
```
86
87
### Untitled Workspace Management
88
89
Service for creating and managing temporary/untitled workspace files.
90
91
```typescript { .api }
92
class UntitledWorkspaceService {
93
/**
94
* Check if a URI represents an untitled workspace
95
* @param candidate - URI to check (optional, defaults to current workspace)
96
*/
97
isUntitledWorkspace(candidate?: URI): boolean;
98
99
/**
100
* Generate a unique URI for an untitled workspace
101
* @param configDirUri - Base configuration directory URI
102
* @param isAcceptable - Function to validate if generated URI is acceptable
103
* @param warnOnHits - Optional callback called after 10 generation attempts
104
*/
105
getUntitledWorkspaceUri(
106
configDirUri: URI,
107
isAcceptable: (candidate: URI) => MaybePromise<boolean>,
108
warnOnHits?: () => unknown
109
): Promise<URI>;
110
}
111
```
112
113
**Usage Example:**
114
115
```typescript
116
import { injectable, inject } from "@theia/core/shared/inversify";
117
import { UntitledWorkspaceService } from "@theia/workspace/lib/common";
118
import { FileService } from "@theia/filesystem/lib/browser/file-service";
119
import URI from "@theia/core/lib/common/uri";
120
121
@injectable()
122
export class MyWorkspaceCreator {
123
124
@inject(UntitledWorkspaceService)
125
protected readonly untitledWorkspaceService: UntitledWorkspaceService;
126
127
@inject(FileService)
128
protected readonly fileService: FileService;
129
130
async createNewUntitledWorkspace(): Promise<URI> {
131
const configDir = new URI("file:///home/user/.theia");
132
133
// Generate unique untitled workspace URI
134
const workspaceUri = await this.untitledWorkspaceService.getUntitledWorkspaceUri(
135
configDir,
136
async (candidate: URI) => {
137
// Check if file already exists
138
try {
139
await this.fileService.resolve(candidate);
140
return false; // File exists, try another name
141
} catch {
142
return true; // File doesn't exist, this name is acceptable
143
}
144
},
145
() => {
146
console.warn("Having trouble finding unique workspace name, trying harder...");
147
}
148
);
149
150
console.log(`Created untitled workspace: ${workspaceUri}`);
151
// Result might be: file:///home/user/.theia/workspaces/Untitled-742.theia-workspace
152
return workspaceUri;
153
}
154
155
checkIfUntitled(workspaceUri: URI): boolean {
156
// Check if a workspace is untitled
157
const isUntitled = this.untitledWorkspaceService.isUntitledWorkspace(workspaceUri);
158
if (isUntitled) {
159
console.log("This is an untitled workspace");
160
}
161
return isUntitled;
162
}
163
164
async createMultipleUntitledWorkspaces(count: number): Promise<URI[]> {
165
const configDir = new URI("file:///home/user/.theia");
166
const workspaces: URI[] = [];
167
168
for (let i = 0; i < count; i++) {
169
try {
170
const uri = await this.untitledWorkspaceService.getUntitledWorkspaceUri(
171
configDir,
172
async (candidate: URI) => {
173
// Ensure no duplicates in our current batch
174
const alreadyUsed = workspaces.some(existing => existing.isEqual(candidate));
175
if (alreadyUsed) return false;
176
177
// Check filesystem
178
try {
179
await this.fileService.resolve(candidate);
180
return false;
181
} catch {
182
return true;
183
}
184
}
185
);
186
workspaces.push(uri);
187
} catch (error) {
188
console.error(`Failed to create untitled workspace ${i + 1}:`, error);
189
break;
190
}
191
}
192
193
return workspaces;
194
}
195
}
196
```
197
198
### File Format Support
199
200
Built-in support for multiple workspace file formats with extensibility.
201
202
```typescript { .api }
203
// Deprecated constants (use WorkspaceFileService methods instead)
204
/** @deprecated Since 1.39.0. Use WorkspaceFileService#getWorkspaceFileTypes instead */
205
const THEIA_EXT = 'theia-workspace';
206
207
/** @deprecated Since 1.39.0. Use WorkspaceFileService#getWorkspaceFileTypes instead */
208
const VSCODE_EXT = 'code-workspace';
209
```
210
211
**Usage Example:**
212
213
```typescript
214
import { injectable, inject } from "@theia/core/shared/inversify";
215
import { WorkspaceFileService } from "@theia/workspace/lib/common";
216
217
@injectable()
218
export class WorkspaceFormatHandler {
219
220
@inject(WorkspaceFileService)
221
protected readonly workspaceFileService: WorkspaceFileService;
222
223
getSupportedFormats(): { [key: string]: string } {
224
const types = this.workspaceFileService.getWorkspaceFileTypes();
225
const formats: { [key: string]: string } = {};
226
227
types.forEach(type => {
228
formats[type.extension] = type.name;
229
});
230
231
return formats;
232
// Returns: { 'theia-workspace': 'Theia', 'code-workspace': 'Visual Studio Code' }
233
}
234
235
createFileFilterString(): string {
236
const types = this.workspaceFileService.getWorkspaceFileTypes();
237
const filters = types.map(type =>
238
`${type.name} Files (*.${type.extension})|*.${type.extension}`
239
);
240
return filters.join('|');
241
// Returns: "Theia Files (*.theia-workspace)|*.theia-workspace|Visual Studio Code Files (*.code-workspace)|*.code-workspace"
242
}
243
244
getPreferredExtension(): string {
245
const types = this.workspaceFileService.getWorkspaceFileTypes();
246
const defaultIndex = this.workspaceFileService.defaultFileTypeIndex;
247
return types[defaultIndex].extension; // 'theia-workspace'
248
}
249
250
isKnownWorkspaceFormat(filename: string): boolean {
251
const extensions = this.workspaceFileService.getWorkspaceFileExtensions();
252
return extensions.some(ext => filename.endsWith(`.${ext}`));
253
}
254
}
255
```
256
257
### Error Handling
258
259
The services include built-in error handling and validation.
260
261
**Example Error Scenarios:**
262
263
```typescript
264
// UntitledWorkspaceService error after too many attempts
265
try {
266
const uri = await this.untitledWorkspaceService.getUntitledWorkspaceUri(
267
configDir,
268
() => false // Always reject to trigger error
269
);
270
} catch (error) {
271
// Error: "Workspace Service: too many attempts to find unused filename."
272
console.error("Could not generate unique workspace name:", error.message);
273
}
274
275
// WorkspaceFileService validation
276
const invalidUri = new URI("file:///project/not-a-workspace.txt");
277
if (!this.workspaceFileService.isWorkspaceFile(invalidUri)) {
278
console.log("File is not a recognized workspace format");
279
}
280
```
281
282
## Types
283
284
```typescript { .api }
285
interface WorkspaceFileType {
286
/** File extension without dot prefix */
287
extension: string;
288
/** Human-readable name for the file type */
289
name: string;
290
}
291
292
type MaybePromise<T> = T | Promise<T>;
293
294
// Deprecated constants
295
/** @deprecated Use WorkspaceFileService methods instead */
296
const THEIA_EXT: string;
297
/** @deprecated Use WorkspaceFileService methods instead */
298
const VSCODE_EXT: string;
299
```