CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-theia--workspace

Theia workspace extension providing workspace functionality and services for Eclipse Theia IDE framework

Pending
Overview
Eval results
Files

workspace-file-handling.mddocs/

Workspace File Handling

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.

Capabilities

Workspace File Type Management

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'
  }
}

Untitled Workspace Management

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;
  }
}

File Format Support

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}`));
  }
}

Error Handling

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");
}

Types

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

docs

extension-points.md

index.md

workspace-commands.md

workspace-file-handling.md

workspace-preferences.md

workspace-server.md

workspace-service.md

tile.json