Theia workspace extension providing workspace functionality and services for Eclipse Theia IDE framework
—
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.
Service for managing supported workspace file formats and validation.
interface WorkspaceFileType {
/** File extension without dot (e.g., 'theia-workspace') */
extension: string;
/** Display name for the file type (e.g., 'Theia') */
name: string;
}
class WorkspaceFileService {
/**
* Index of the default workspace file type (currently 0 for Theia format)
*/
readonly defaultFileTypeIndex: number;
/**
* Check if the file should be considered as a workspace file
* @param candidate - FileStat object or URI to check
*/
isWorkspaceFile(candidate: FileStat | URI): boolean;
/**
* Get all supported workspace file types
*/
getWorkspaceFileTypes(): WorkspaceFileType[];
/**
* Get workspace file extensions, optionally with dot prefix
* @param dot - Whether to include dot prefix (e.g., '.theia-workspace')
*/
getWorkspaceFileExtensions(dot?: boolean): string[];
}Usage Example:
import { injectable, inject } from "@theia/core/shared/inversify";
import { WorkspaceFileService, WorkspaceFileType } from "@theia/workspace/lib/common";
import URI from "@theia/core/lib/common/uri";
@injectable()
export class MyWorkspaceValidator {
@inject(WorkspaceFileService)
protected readonly workspaceFileService: WorkspaceFileService;
validateWorkspaceFile(uri: URI): boolean {
// Check if file is a valid workspace file
if (!this.workspaceFileService.isWorkspaceFile(uri)) {
console.log(`${uri} is not a valid workspace file`);
return false;
}
return true;
}
getSupportedFormats(): WorkspaceFileType[] {
// Get all supported workspace file types
const types = this.workspaceFileService.getWorkspaceFileTypes();
// Returns: [{ name: 'Theia', extension: 'theia-workspace' },
// { name: 'Visual Studio Code', extension: 'code-workspace' }]
return types;
}
createFileFilter(): string {
// Create file filter for file dialogs
const extensions = this.workspaceFileService.getWorkspaceFileExtensions(true);
return extensions.join(','); // '.theia-workspace,.code-workspace'
}
getDefaultExtension(): string {
const types = this.workspaceFileService.getWorkspaceFileTypes();
const defaultIndex = this.workspaceFileService.defaultFileTypeIndex;
return types[defaultIndex].extension; // 'theia-workspace'
}
}Service for creating and managing temporary/untitled workspace files.
class UntitledWorkspaceService {
/**
* Check if a URI represents an untitled workspace
* @param candidate - URI to check (optional, defaults to current workspace)
*/
isUntitledWorkspace(candidate?: URI): boolean;
/**
* Generate a unique URI for an untitled workspace
* @param configDirUri - Base configuration directory URI
* @param isAcceptable - Function to validate if generated URI is acceptable
* @param warnOnHits - Optional callback called after 10 generation attempts
*/
getUntitledWorkspaceUri(
configDirUri: URI,
isAcceptable: (candidate: URI) => MaybePromise<boolean>,
warnOnHits?: () => unknown
): Promise<URI>;
}Usage Example:
import { injectable, inject } from "@theia/core/shared/inversify";
import { UntitledWorkspaceService } from "@theia/workspace/lib/common";
import { FileService } from "@theia/filesystem/lib/browser/file-service";
import URI from "@theia/core/lib/common/uri";
@injectable()
export class MyWorkspaceCreator {
@inject(UntitledWorkspaceService)
protected readonly untitledWorkspaceService: UntitledWorkspaceService;
@inject(FileService)
protected readonly fileService: FileService;
async createNewUntitledWorkspace(): Promise<URI> {
const configDir = new URI("file:///home/user/.theia");
// Generate unique untitled workspace URI
const workspaceUri = await this.untitledWorkspaceService.getUntitledWorkspaceUri(
configDir,
async (candidate: URI) => {
// Check if file already exists
try {
await this.fileService.resolve(candidate);
return false; // File exists, try another name
} catch {
return true; // File doesn't exist, this name is acceptable
}
},
() => {
console.warn("Having trouble finding unique workspace name, trying harder...");
}
);
console.log(`Created untitled workspace: ${workspaceUri}`);
// Result might be: file:///home/user/.theia/workspaces/Untitled-742.theia-workspace
return workspaceUri;
}
checkIfUntitled(workspaceUri: URI): boolean {
// Check if a workspace is untitled
const isUntitled = this.untitledWorkspaceService.isUntitledWorkspace(workspaceUri);
if (isUntitled) {
console.log("This is an untitled workspace");
}
return isUntitled;
}
async createMultipleUntitledWorkspaces(count: number): Promise<URI[]> {
const configDir = new URI("file:///home/user/.theia");
const workspaces: URI[] = [];
for (let i = 0; i < count; i++) {
try {
const uri = await this.untitledWorkspaceService.getUntitledWorkspaceUri(
configDir,
async (candidate: URI) => {
// Ensure no duplicates in our current batch
const alreadyUsed = workspaces.some(existing => existing.isEqual(candidate));
if (alreadyUsed) return false;
// Check filesystem
try {
await this.fileService.resolve(candidate);
return false;
} catch {
return true;
}
}
);
workspaces.push(uri);
} catch (error) {
console.error(`Failed to create untitled workspace ${i + 1}:`, error);
break;
}
}
return workspaces;
}
}Built-in support for multiple workspace file formats with extensibility.
// Deprecated constants (use WorkspaceFileService methods instead)
/** @deprecated Since 1.39.0. Use WorkspaceFileService#getWorkspaceFileTypes instead */
const THEIA_EXT = 'theia-workspace';
/** @deprecated Since 1.39.0. Use WorkspaceFileService#getWorkspaceFileTypes instead */
const VSCODE_EXT = 'code-workspace';Usage Example:
import { injectable, inject } from "@theia/core/shared/inversify";
import { WorkspaceFileService } from "@theia/workspace/lib/common";
@injectable()
export class WorkspaceFormatHandler {
@inject(WorkspaceFileService)
protected readonly workspaceFileService: WorkspaceFileService;
getSupportedFormats(): { [key: string]: string } {
const types = this.workspaceFileService.getWorkspaceFileTypes();
const formats: { [key: string]: string } = {};
types.forEach(type => {
formats[type.extension] = type.name;
});
return formats;
// Returns: { 'theia-workspace': 'Theia', 'code-workspace': 'Visual Studio Code' }
}
createFileFilterString(): string {
const types = this.workspaceFileService.getWorkspaceFileTypes();
const filters = types.map(type =>
`${type.name} Files (*.${type.extension})|*.${type.extension}`
);
return filters.join('|');
// Returns: "Theia Files (*.theia-workspace)|*.theia-workspace|Visual Studio Code Files (*.code-workspace)|*.code-workspace"
}
getPreferredExtension(): string {
const types = this.workspaceFileService.getWorkspaceFileTypes();
const defaultIndex = this.workspaceFileService.defaultFileTypeIndex;
return types[defaultIndex].extension; // 'theia-workspace'
}
isKnownWorkspaceFormat(filename: string): boolean {
const extensions = this.workspaceFileService.getWorkspaceFileExtensions();
return extensions.some(ext => filename.endsWith(`.${ext}`));
}
}The services include built-in error handling and validation.
Example Error Scenarios:
// UntitledWorkspaceService error after too many attempts
try {
const uri = await this.untitledWorkspaceService.getUntitledWorkspaceUri(
configDir,
() => false // Always reject to trigger error
);
} catch (error) {
// Error: "Workspace Service: too many attempts to find unused filename."
console.error("Could not generate unique workspace name:", error.message);
}
// WorkspaceFileService validation
const invalidUri = new URI("file:///project/not-a-workspace.txt");
if (!this.workspaceFileService.isWorkspaceFile(invalidUri)) {
console.log("File is not a recognized workspace format");
}interface WorkspaceFileType {
/** File extension without dot prefix */
extension: string;
/** Human-readable name for the file type */
name: string;
}
type MaybePromise<T> = T | Promise<T>;
// Deprecated constants
/** @deprecated Use WorkspaceFileService methods instead */
const THEIA_EXT: string;
/** @deprecated Use WorkspaceFileService methods instead */
const VSCODE_EXT: string;Install with Tessl CLI
npx tessl i tessl/npm-theia--workspace