Skip to content

Language Services API

The Language Services API provides language-specific features like syntax highlighting, diagnostics, hover information, and intelligent code analysis.

Overview

The Language Services API enables you to:

  • Provide syntax highlighting and tokenization
  • Generate diagnostic information (errors, warnings)
  • Offer hover information and documentation
  • Implement go-to-definition and find references
  • Support code formatting and refactoring
  • Create language-specific completion providers
  • Handle semantic analysis and type checking

Basic Usage

Registering Language Features

typescript
import { TraeAPI } from '@trae/api';

// Register a hover provider
const hoverProvider = TraeAPI.languages.registerHoverProvider(
  { scheme: 'file', language: 'typescript' },
  {
    async provideHover(document, position, token) {
      const wordRange = document.getWordRangeAtPosition(position);
      if (!wordRange) {
        return;
      }

      const word = document.getText(wordRange);
      const hoverContent = await getHoverInformation(word, document, position);
      
      if (hoverContent) {
        return new TraeAPI.Hover(hoverContent, wordRange);
      }
    }
  }
);

// Register a definition provider
const definitionProvider = TraeAPI.languages.registerDefinitionProvider(
  { scheme: 'file', language: 'typescript' },
  {
    async provideDefinition(document, position, token) {
      const wordRange = document.getWordRangeAtPosition(position);
      if (!wordRange) {
        return;
      }

      const word = document.getText(wordRange);
      const definitions = await findDefinitions(word, document, position);
      
      return definitions.map(def => new TraeAPI.Location(
        TraeAPI.Uri.file(def.file),
        new TraeAPI.Position(def.line, def.character)
      ));
    }
  }
);

async function getHoverInformation(word: string, document: TraeAPI.TextDocument, position: TraeAPI.Position): Promise<TraeAPI.MarkdownString | undefined> {
  // Implementation would analyze the code and return hover information
  const markdown = new TraeAPI.MarkdownString();
  markdown.appendCodeblock(`function ${word}(): void`, 'typescript');
  markdown.appendMarkdown('\n\nThis is a TypeScript function.');
  return markdown;
}

async function findDefinitions(word: string, document: TraeAPI.TextDocument, position: TraeAPI.Position): Promise<Array<{ file: string; line: number; character: number }>> {
  // Implementation would find all definitions of the symbol
  return [
    { file: document.fileName, line: 10, character: 5 }
  ];
}

Diagnostic Provider

typescript
class DiagnosticProvider {
  private diagnosticCollection: TraeAPI.DiagnosticCollection;

  constructor() {
    this.diagnosticCollection = TraeAPI.languages.createDiagnosticCollection('myLanguage');
    this.setupDocumentListener();
  }

  private setupDocumentListener() {
    // Listen for document changes and provide diagnostics
    TraeAPI.workspace.onDidChangeTextDocument(event => {
      this.updateDiagnostics(event.document);
    });

    TraeAPI.workspace.onDidOpenTextDocument(document => {
      this.updateDiagnostics(document);
    });
  }

  private async updateDiagnostics(document: TraeAPI.TextDocument) {
    if (document.languageId !== 'typescript') {
      return;
    }

    const diagnostics = await this.analyzDocument(document);
    this.diagnosticCollection.set(document.uri, diagnostics);
  }

  private async analyzDocument(document: TraeAPI.TextDocument): Promise<TraeAPI.Diagnostic[]> {
    const diagnostics: TraeAPI.Diagnostic[] = [];
    const text = document.getText();
    const lines = text.split('\n');

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      
      // Check for syntax errors
      if (line.includes('console.log') && !line.includes(';')) {
        const range = new TraeAPI.Range(
          new TraeAPI.Position(i, line.indexOf('console.log')),
          new TraeAPI.Position(i, line.length)
        );
        
        const diagnostic = new TraeAPI.Diagnostic(
          range,
          'Missing semicolon',
          TraeAPI.DiagnosticSeverity.Warning
        );
        diagnostic.code = 'missing-semicolon';
        diagnostic.source = 'myLanguageServer';
        
        diagnostics.push(diagnostic);
      }

      // Check for unused variables
      const variableMatch = line.match(/(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
      if (variableMatch) {
        const variableName = variableMatch[1];
        const isUsed = this.isVariableUsed(variableName, text, i);
        
        if (!isUsed) {
          const range = new TraeAPI.Range(
            new TraeAPI.Position(i, variableMatch.index!),
            new TraeAPI.Position(i, variableMatch.index! + variableMatch[0].length)
          );
          
          const diagnostic = new TraeAPI.Diagnostic(
            range,
            `Variable '${variableName}' is declared but never used`,
            TraeAPI.DiagnosticSeverity.Information
          );
          diagnostic.code = 'unused-variable';
          diagnostic.tags = [TraeAPI.DiagnosticTag.Unnecessary];
          
          diagnostics.push(diagnostic);
        }
      }

      // Check for deprecated APIs
      if (line.includes('oldFunction')) {
        const range = new TraeAPI.Range(
          new TraeAPI.Position(i, line.indexOf('oldFunction')),
          new TraeAPI.Position(i, line.indexOf('oldFunction') + 'oldFunction'.length)
        );
        
        const diagnostic = new TraeAPI.Diagnostic(
          range,
          'oldFunction is deprecated. Use newFunction instead.',
          TraeAPI.DiagnosticSeverity.Warning
        );
        diagnostic.tags = [TraeAPI.DiagnosticTag.Deprecated];
        
        diagnostics.push(diagnostic);
      }
    }

    return diagnostics;
  }

  private isVariableUsed(variableName: string, text: string, declarationLine: number): boolean {
    const lines = text.split('\n');
    
    // Check lines after declaration
    for (let i = declarationLine + 1; i < lines.length; i++) {
      if (lines[i].includes(variableName)) {
        return true;
      }
    }
    
    return false;
  }

  dispose() {
    this.diagnosticCollection.dispose();
  }
}

// Initialize diagnostic provider
const diagnosticProvider = new DiagnosticProvider();

Advanced Language Features

Code Actions Provider

typescript
class CodeActionsProvider implements TraeAPI.CodeActionProvider {
  async provideCodeActions(
    document: TraeAPI.TextDocument,
    range: TraeAPI.Range,
    context: TraeAPI.CodeActionContext,
    token: TraeAPI.CancellationToken
  ): Promise<TraeAPI.CodeAction[]> {
    const actions: TraeAPI.CodeAction[] = [];

    // Quick fixes for diagnostics
    for (const diagnostic of context.diagnostics) {
      if (diagnostic.code === 'missing-semicolon') {
        const fixAction = new TraeAPI.CodeAction(
          'Add semicolon',
          TraeAPI.CodeActionKind.QuickFix
        );
        
        fixAction.edit = new TraeAPI.WorkspaceEdit();
        fixAction.edit.insert(
          document.uri,
          diagnostic.range.end,
          ';'
        );
        
        fixAction.diagnostics = [diagnostic];
        actions.push(fixAction);
      }

      if (diagnostic.code === 'unused-variable') {
        const removeAction = new TraeAPI.CodeAction(
          'Remove unused variable',
          TraeAPI.CodeActionKind.QuickFix
        );
        
        removeAction.edit = new TraeAPI.WorkspaceEdit();
        const line = document.lineAt(diagnostic.range.start.line);
        removeAction.edit.delete(
          document.uri,
          line.rangeIncludingLineBreak
        );
        
        removeAction.diagnostics = [diagnostic];
        actions.push(removeAction);
      }
    }

    // Refactoring actions
    const selectedText = document.getText(range);
    if (selectedText && !range.isEmpty) {
      // Extract to function
      const extractAction = new TraeAPI.CodeAction(
        'Extract to function',
        TraeAPI.CodeActionKind.Refactor
      );
      
      extractAction.edit = await this.createExtractFunctionEdit(document, range, selectedText);
      actions.push(extractAction);

      // Extract to variable
      const extractVarAction = new TraeAPI.CodeAction(
        'Extract to variable',
        TraeAPI.CodeActionKind.Refactor
      );
      
      extractVarAction.edit = await this.createExtractVariableEdit(document, range, selectedText);
      actions.push(extractVarAction);
    }

    // Source actions
    const organizeImportsAction = new TraeAPI.CodeAction(
      'Organize imports',
      TraeAPI.CodeActionKind.SourceOrganizeImports
    );
    
    organizeImportsAction.edit = await this.createOrganizeImportsEdit(document);
    actions.push(organizeImportsAction);

    return actions;
  }

  private async createExtractFunctionEdit(
    document: TraeAPI.TextDocument,
    range: TraeAPI.Range,
    selectedText: string
  ): Promise<TraeAPI.WorkspaceEdit> {
    const edit = new TraeAPI.WorkspaceEdit();
    const functionName = 'extractedFunction';
    
    // Replace selected text with function call
    edit.replace(document.uri, range, `${functionName}()`);
    
    // Add function definition
    const functionDef = `\nfunction ${functionName}() {\n  ${selectedText}\n}\n`;
    edit.insert(document.uri, new TraeAPI.Position(0, 0), functionDef);
    
    return edit;
  }

  private async createExtractVariableEdit(
    document: TraeAPI.TextDocument,
    range: TraeAPI.Range,
    selectedText: string
  ): Promise<TraeAPI.WorkspaceEdit> {
    const edit = new TraeAPI.WorkspaceEdit();
    const variableName = 'extractedVariable';
    
    // Replace selected text with variable reference
    edit.replace(document.uri, range, variableName);
    
    // Add variable declaration
    const variableDef = `const ${variableName} = ${selectedText};\n`;
    edit.insert(document.uri, new TraeAPI.Position(range.start.line, 0), variableDef);
    
    return edit;
  }

  private async createOrganizeImportsEdit(document: TraeAPI.TextDocument): Promise<TraeAPI.WorkspaceEdit> {
    const edit = new TraeAPI.WorkspaceEdit();
    const text = document.getText();
    const lines = text.split('\n');
    
    // Find and organize import statements
    const imports: string[] = [];
    const nonImportLines: string[] = [];
    
    for (const line of lines) {
      if (line.trim().startsWith('import ')) {
        imports.push(line);
      } else {
        nonImportLines.push(line);
      }
    }
    
    // Sort imports
    imports.sort();
    
    // Reconstruct file content
    const newContent = [...imports, '', ...nonImportLines].join('\n');
    
    edit.replace(
      document.uri,
      new TraeAPI.Range(0, 0, document.lineCount, 0),
      newContent
    );
    
    return edit;
  }
}

// Register code actions provider
const codeActionsProvider = new CodeActionsProvider();
TraeAPI.languages.registerCodeActionsProvider(
  { scheme: 'file', language: 'typescript' },
  codeActionsProvider
);

Document Formatting Provider

typescript
class FormattingProvider implements TraeAPI.DocumentFormattingEditProvider {
  async provideDocumentFormattingEdits(
    document: TraeAPI.TextDocument,
    options: TraeAPI.FormattingOptions,
    token: TraeAPI.CancellationToken
  ): Promise<TraeAPI.TextEdit[]> {
    const edits: TraeAPI.TextEdit[] = [];
    const text = document.getText();
    const formattedText = await this.formatCode(text, options);
    
    if (formattedText !== text) {
      const fullRange = new TraeAPI.Range(
        0, 0,
        document.lineCount, 0
      );
      
      edits.push(TraeAPI.TextEdit.replace(fullRange, formattedText));
    }
    
    return edits;
  }

  async provideDocumentRangeFormattingEdits(
    document: TraeAPI.TextDocument,
    range: TraeAPI.Range,
    options: TraeAPI.FormattingOptions,
    token: TraeAPI.CancellationToken
  ): Promise<TraeAPI.TextEdit[]> {
    const edits: TraeAPI.TextEdit[] = [];
    const rangeText = document.getText(range);
    const formattedText = await this.formatCode(rangeText, options);
    
    if (formattedText !== rangeText) {
      edits.push(TraeAPI.TextEdit.replace(range, formattedText));
    }
    
    return edits;
  }

  private async formatCode(code: string, options: TraeAPI.FormattingOptions): Promise<string> {
    // Simple formatting implementation
    let formatted = code;
    
    // Fix indentation
    const lines = formatted.split('\n');
    let indentLevel = 0;
    const indentString = options.insertSpaces ? ' '.repeat(options.tabSize) : '\t';
    
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      
      if (line.endsWith('{')) {
        lines[i] = indentString.repeat(indentLevel) + line;
        indentLevel++;
      } else if (line.startsWith('}')) {
        indentLevel = Math.max(0, indentLevel - 1);
        lines[i] = indentString.repeat(indentLevel) + line;
      } else if (line) {
        lines[i] = indentString.repeat(indentLevel) + line;
      }
    }
    
    formatted = lines.join('\n');
    
    // Add missing semicolons
    formatted = formatted.replace(/([^;{}])\n/g, '$1;\n');
    
    // Fix spacing around operators
    formatted = formatted.replace(/([a-zA-Z0-9])([=+\-*/])([a-zA-Z0-9])/g, '$1 $2 $3');
    
    return formatted;
  }
}

// Register formatting providers
const formattingProvider = new FormattingProvider();
TraeAPI.languages.registerDocumentFormattingEditProvider(
  { scheme: 'file', language: 'typescript' },
  formattingProvider
);
TraeAPI.languages.registerDocumentRangeFormattingEditProvider(
  { scheme: 'file', language: 'typescript' },
  formattingProvider
);

Symbol Provider

typescript
class DocumentSymbolProvider implements TraeAPI.DocumentSymbolProvider {
  async provideDocumentSymbols(
    document: TraeAPI.TextDocument,
    token: TraeAPI.CancellationToken
  ): Promise<TraeAPI.DocumentSymbol[]> {
    const symbols: TraeAPI.DocumentSymbol[] = [];
    const text = document.getText();
    const lines = text.split('\n');
    
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      
      // Find functions
      const functionMatch = line.match(/function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(([^)]*)\)/);
      if (functionMatch) {
        const name = functionMatch[1];
        const params = functionMatch[2];
        
        const symbol = new TraeAPI.DocumentSymbol(
          name,
          `(${params})`,
          TraeAPI.SymbolKind.Function,
          new TraeAPI.Range(i, 0, i, line.length),
          new TraeAPI.Range(i, functionMatch.index!, i, functionMatch.index! + functionMatch[0].length)
        );
        
        symbols.push(symbol);
      }
      
      // Find classes
      const classMatch = line.match(/class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
      if (classMatch) {
        const name = classMatch[1];
        
        const symbol = new TraeAPI.DocumentSymbol(
          name,
          '',
          TraeAPI.SymbolKind.Class,
          new TraeAPI.Range(i, 0, i, line.length),
          new TraeAPI.Range(i, classMatch.index!, i, classMatch.index! + classMatch[0].length)
        );
        
        // Find class methods
        const methods = this.findClassMethods(lines, i);
        symbol.children = methods;
        
        symbols.push(symbol);
      }
      
      // Find variables
      const variableMatch = line.match(/(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
      if (variableMatch) {
        const name = variableMatch[1];
        
        const symbol = new TraeAPI.DocumentSymbol(
          name,
          '',
          TraeAPI.SymbolKind.Variable,
          new TraeAPI.Range(i, 0, i, line.length),
          new TraeAPI.Range(i, variableMatch.index!, i, variableMatch.index! + variableMatch[0].length)
        );
        
        symbols.push(symbol);
      }
    }
    
    return symbols;
  }
  
  private findClassMethods(lines: string[], classStartLine: number): TraeAPI.DocumentSymbol[] {
    const methods: TraeAPI.DocumentSymbol[] = [];
    let braceCount = 0;
    let inClass = false;
    
    for (let i = classStartLine; i < lines.length; i++) {
      const line = lines[i];
      
      if (line.includes('{')) {
        braceCount++;
        inClass = true;
      }
      
      if (line.includes('}')) {
        braceCount--;
        if (braceCount === 0) {
          break;
        }
      }
      
      if (inClass && braceCount === 1) {
        const methodMatch = line.match(/([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(([^)]*)\)\s*{/);
        if (methodMatch) {
          const name = methodMatch[1];
          const params = methodMatch[2];
          
          const method = new TraeAPI.DocumentSymbol(
            name,
            `(${params})`,
            TraeAPI.SymbolKind.Method,
            new TraeAPI.Range(i, 0, i, line.length),
            new TraeAPI.Range(i, methodMatch.index!, i, methodMatch.index! + methodMatch[0].length)
          );
          
          methods.push(method);
        }
      }
    }
    
    return methods;
  }
}

// Register symbol provider
const symbolProvider = new DocumentSymbolProvider();
TraeAPI.languages.registerDocumentSymbolProvider(
  { scheme: 'file', language: 'typescript' },
  symbolProvider
);

Reference Provider

typescript
class ReferenceProvider implements TraeAPI.ReferenceProvider {
  async provideReferences(
    document: TraeAPI.TextDocument,
    position: TraeAPI.Position,
    context: TraeAPI.ReferenceContext,
    token: TraeAPI.CancellationToken
  ): Promise<TraeAPI.Location[]> {
    const wordRange = document.getWordRangeAtPosition(position);
    if (!wordRange) {
      return [];
    }
    
    const word = document.getText(wordRange);
    const references: TraeAPI.Location[] = [];
    
    // Search in current document
    const currentDocRefs = this.findReferencesInDocument(document, word);
    references.push(...currentDocRefs);
    
    // Search in workspace
    const workspaceRefs = await this.findReferencesInWorkspace(word, document.uri);
    references.push(...workspaceRefs);
    
    // Include declaration if requested
    if (context.includeDeclaration) {
      const declaration = await this.findDeclaration(word, document);
      if (declaration) {
        references.push(declaration);
      }
    }
    
    return references;
  }
  
  private findReferencesInDocument(document: TraeAPI.TextDocument, word: string): TraeAPI.Location[] {
    const references: TraeAPI.Location[] = [];
    const text = document.getText();
    const regex = new RegExp(`\\b${word}\\b`, 'g');
    let match;
    
    while ((match = regex.exec(text)) !== null) {
      const position = document.positionAt(match.index);
      const range = new TraeAPI.Range(
        position,
        document.positionAt(match.index + word.length)
      );
      
      references.push(new TraeAPI.Location(document.uri, range));
    }
    
    return references;
  }
  
  private async findReferencesInWorkspace(word: string, excludeUri: TraeAPI.Uri): Promise<TraeAPI.Location[]> {
    const references: TraeAPI.Location[] = [];
    
    // Find all TypeScript files in workspace
    const files = await TraeAPI.workspace.findFiles('**/*.{ts,js}', '**/node_modules/**');
    
    for (const file of files) {
      if (file.toString() === excludeUri.toString()) {
        continue;
      }
      
      try {
        const document = await TraeAPI.workspace.openTextDocument(file);
        const fileRefs = this.findReferencesInDocument(document, word);
        references.push(...fileRefs);
      } catch (error) {
        console.error('Error reading file:', file.toString(), error);
      }
    }
    
    return references;
  }
  
  private async findDeclaration(word: string, document: TraeAPI.TextDocument): Promise<TraeAPI.Location | undefined> {
    const text = document.getText();
    const declarationRegex = new RegExp(`(?:function|class|const|let|var)\\s+${word}\\b`);
    const match = declarationRegex.exec(text);
    
    if (match) {
      const position = document.positionAt(match.index);
      const range = new TraeAPI.Range(
        position,
        document.positionAt(match.index + match[0].length)
      );
      
      return new TraeAPI.Location(document.uri, range);
    }
    
    return undefined;
  }
}

// Register reference provider
const referenceProvider = new ReferenceProvider();
TraeAPI.languages.registerReferenceProvider(
  { scheme: 'file', language: 'typescript' },
  referenceProvider
);

Language Server Integration

Custom Language Server

typescript
import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';

class CustomLanguageServer {
  private client: LanguageClient | undefined;
  
  async start() {
    // Server options
    const serverOptions: ServerOptions = {
      command: 'node',
      args: ['/path/to/language-server.js'],
      options: {
        env: {
          ...process.env,
          NODE_ENV: 'production'
        }
      }
    };
    
    // Client options
    const clientOptions: LanguageClientOptions = {
      documentSelector: [
        { scheme: 'file', language: 'mylanguage' }
      ],
      synchronize: {
        fileEvents: TraeAPI.workspace.createFileSystemWatcher('**/*.mylang')
      },
      initializationOptions: {
        settings: {
          maxNumberOfProblems: 100,
          enableCodeActions: true,
          enableCompletion: true
        }
      }
    };
    
    // Create and start the client
    this.client = new LanguageClient(
      'myLanguageServer',
      'My Language Server',
      serverOptions,
      clientOptions
    );
    
    await this.client.start();
    console.log('Language server started');
  }
  
  async stop() {
    if (this.client) {
      await this.client.stop();
      this.client = undefined;
      console.log('Language server stopped');
    }
  }
  
  async sendCustomRequest(method: string, params: any): Promise<any> {
    if (!this.client) {
      throw new Error('Language server not started');
    }
    
    return await this.client.sendRequest(method, params);
  }
  
  onCustomNotification(method: string, handler: (params: any) => void) {
    if (!this.client) {
      throw new Error('Language server not started');
    }
    
    this.client.onNotification(method, handler);
  }
}

// Initialize language server
const languageServer = new CustomLanguageServer();
languageServer.start();

Semantic Highlighting

Token Provider

typescript
class SemanticTokensProvider implements TraeAPI.DocumentSemanticTokensProvider {
  private readonly legend: TraeAPI.SemanticTokensLegend;
  
  constructor() {
    this.legend = new TraeAPI.SemanticTokensLegend(
      ['class', 'function', 'variable', 'parameter', 'property', 'keyword'],
      ['declaration', 'definition', 'readonly', 'static', 'deprecated']
    );
  }
  
  async provideDocumentSemanticTokens(
    document: TraeAPI.TextDocument,
    token: TraeAPI.CancellationToken
  ): Promise<TraeAPI.SemanticTokens> {
    const builder = new TraeAPI.SemanticTokensBuilder(this.legend);
    const text = document.getText();
    const lines = text.split('\n');
    
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      const line = lines[lineIndex];
      
      // Tokenize functions
      const functionRegex = /\b(function)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g;
      let match;
      
      while ((match = functionRegex.exec(line)) !== null) {
        // 'function' keyword
        builder.push(
          lineIndex,
          match.index,
          match[1].length,
          this.legend.tokenTypes.indexOf('keyword'),
          0
        );
        
        // Function name
        builder.push(
          lineIndex,
          match.index + match[1].length + 1,
          match[2].length,
          this.legend.tokenTypes.indexOf('function'),
          this.encodeModifiers(['declaration'])
        );
      }
      
      // Tokenize classes
      const classRegex = /\b(class)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
      
      while ((match = classRegex.exec(line)) !== null) {
        // 'class' keyword
        builder.push(
          lineIndex,
          match.index,
          match[1].length,
          this.legend.tokenTypes.indexOf('keyword'),
          0
        );
        
        // Class name
        builder.push(
          lineIndex,
          match.index + match[1].length + 1,
          match[2].length,
          this.legend.tokenTypes.indexOf('class'),
          this.encodeModifiers(['declaration'])
        );
      }
      
      // Tokenize variables
      const variableRegex = /\b(const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
      
      while ((match = variableRegex.exec(line)) !== null) {
        // Variable keyword
        builder.push(
          lineIndex,
          match.index,
          match[1].length,
          this.legend.tokenTypes.indexOf('keyword'),
          0
        );
        
        // Variable name
        const modifiers = match[1] === 'const' ? ['readonly'] : [];
        builder.push(
          lineIndex,
          match.index + match[1].length + 1,
          match[2].length,
          this.legend.tokenTypes.indexOf('variable'),
          this.encodeModifiers(modifiers)
        );
      }
    }
    
    return builder.build();
  }
  
  private encodeModifiers(modifiers: string[]): number {
    let result = 0;
    for (const modifier of modifiers) {
      const index = this.legend.tokenModifiers.indexOf(modifier);
      if (index !== -1) {
        result |= (1 << index);
      }
    }
    return result;
  }
}

// Register semantic tokens provider
const semanticTokensProvider = new SemanticTokensProvider();
TraeAPI.languages.registerDocumentSemanticTokensProvider(
  { scheme: 'file', language: 'typescript' },
  semanticTokensProvider,
  semanticTokensProvider.legend
);

API Reference

Core Interfaces

typescript
interface HoverProvider {
  provideHover(
    document: TextDocument,
    position: Position,
    token: CancellationToken
  ): ProviderResult<Hover>;
}

interface DefinitionProvider {
  provideDefinition(
    document: TextDocument,
    position: Position,
    token: CancellationToken
  ): ProviderResult<Definition | DefinitionLink[]>;
}

interface CodeActionProvider {
  provideCodeActions(
    document: TextDocument,
    range: Range,
    context: CodeActionContext,
    token: CancellationToken
  ): ProviderResult<CodeAction[]>;
}

interface DocumentFormattingEditProvider {
  provideDocumentFormattingEdits(
    document: TextDocument,
    options: FormattingOptions,
    token: CancellationToken
  ): ProviderResult<TextEdit[]>;
}

interface DiagnosticCollection {
  readonly name: string;
  set(uri: Uri, diagnostics: Diagnostic[]): void;
  delete(uri: Uri): void;
  clear(): void;
  forEach(callback: (uri: Uri, diagnostics: Diagnostic[]) => void): void;
  dispose(): void;
}

Best Practices

  1. Performance: Cache analysis results and use incremental updates
  2. Accuracy: Provide precise ranges and positions for all features
  3. User Experience: Show progress for long-running operations
  4. Error Handling: Handle malformed code gracefully
  5. Memory Management: Dispose of providers and collections properly
  6. Consistency: Follow language-specific conventions and standards
  7. Extensibility: Design providers to be easily extended
  8. Testing: Thoroughly test with various code patterns

究極の AI 駆動 IDE 学習ガイド