Skip to content

语言服务 API

概述

Trae 语言服务 API 提供了丰富的编程语言支持功能,包括语法高亮、代码补全、错误诊断、符号导航、重构等。开发者可以通过 API 扩展现有语言支持或添加新的编程语言。

核心概念

语言标识符

typescript
// 语言标识符定义
interface LanguageIdentifier {
  readonly language: string;
  readonly scheme?: string;
}

// 常见语言标识符
const LANGUAGES = {
  TYPESCRIPT: 'typescript',
  JAVASCRIPT: 'javascript',
  PYTHON: 'python',
  JAVA: 'java',
  CSHARP: 'csharp',
  CPP: 'cpp',
  RUST: 'rust',
  GO: 'go',
  PHP: 'php',
  RUBY: 'ruby'
} as const;

文档选择器

typescript
interface DocumentSelector {
  language?: string;
  scheme?: string;
  pattern?: string;
}

type DocumentFilter = string | DocumentSelector;

// 示例文档选择器
const typescriptSelector: DocumentSelector = {
  language: 'typescript',
  scheme: 'file'
};

const configSelector: DocumentSelector = {
  pattern: '**/*.{json,yaml,yml}'
};

API 参考

语言注册

注册语言

typescript
import { languages, Disposable } from '@trae/api';

// 注册新语言
const registerLanguage = (languageId: string): Disposable => {
  return languages.registerLanguage({
    id: languageId,
    extensions: [`.${languageId}`],
    aliases: [languageId],
    mimetypes: [`text/x-${languageId}`]
  });
};

// 设置语言配置
const setLanguageConfiguration = (
  languageId: string,
  configuration: LanguageConfiguration
): Disposable => {
  return languages.setLanguageConfiguration(languageId, configuration);
};

interface LanguageConfiguration {
  comments?: CommentRule;
  brackets?: CharacterPair[];
  wordPattern?: RegExp;
  indentationRules?: IndentationRule;
  onEnterRules?: OnEnterRule[];
  autoClosingPairs?: AutoClosingPairConditional[];
  surroundingPairs?: AutoClosingPair[];
  colorizedBracketPairs?: CharacterPair[];
}

interface CommentRule {
  lineComment?: string;
  blockComment?: CharacterPair;
}

type CharacterPair = [string, string];

语言配置示例

typescript
// TypeScript 语言配置
const typescriptConfig: LanguageConfiguration = {
  comments: {
    lineComment: '//',
    blockComment: ['/*', '*/']
  },
  brackets: [
    ['{', '}'],
    ['[', ']'],
    ['(', ')']
  ],
  autoClosingPairs: [
    { open: '{', close: '}' },
    { open: '[', close: ']' },
    { open: '(', close: ')' },
    { open: '"', close: '"', notIn: ['string'] },
    { open: "'", close: "'", notIn: ['string', 'comment'] },
    { open: '`', close: '`', notIn: ['string', 'comment'] }
  ],
  surroundingPairs: [
    { open: '{', close: '}' },
    { open: '[', close: ']' },
    { open: '(', close: ')' },
    { open: '"', close: '"' },
    { open: "'", close: "'" },
    { open: '`', close: '`' }
  ],
  wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g
};

// 注册 TypeScript 语言
const disposable = setLanguageConfiguration('typescript', typescriptConfig);

代码补全

补全提供者

typescript
// 注册补全提供者
const registerCompletionProvider = (
  selector: DocumentSelector,
  provider: CompletionItemProvider,
  ...triggerCharacters: string[]
): Disposable => {
  return languages.registerCompletionItemProvider(
    selector,
    provider,
    ...triggerCharacters
  );
};

interface CompletionItemProvider {
  provideCompletionItems(
    document: TextDocument,
    position: Position,
    token: CancellationToken,
    context: CompletionContext
  ): ProviderResult<CompletionItem[] | CompletionList>;
  
  resolveCompletionItem?(
    item: CompletionItem,
    token: CancellationToken
  ): ProviderResult<CompletionItem>;
}

interface CompletionItem {
  label: string | CompletionItemLabel;
  kind?: CompletionItemKind;
  tags?: CompletionItemTag[];
  detail?: string;
  documentation?: string | MarkdownString;
  sortText?: string;
  filterText?: string;
  preselect?: boolean;
  insertText?: string | SnippetString;
  range?: Range | { inserting: Range; replacing: Range };
  commitCharacters?: string[];
  keepWhitespace?: boolean;
  additionalTextEdits?: TextEdit[];
  command?: Command;
}

补全提供者实现示例

typescript
class CustomCompletionProvider implements CompletionItemProvider {
  private keywords = ['function', 'class', 'interface', 'type', 'const', 'let', 'var'];
  
  provideCompletionItems(
    document: TextDocument,
    position: Position,
    token: CancellationToken,
    context: CompletionContext
  ): CompletionItem[] {
    const items: CompletionItem[] = [];
    
    // 添加关键字补全
    for (const keyword of this.keywords) {
      items.push({
        label: keyword,
        kind: CompletionItemKind.Keyword,
        insertText: keyword,
        documentation: `${keyword} keyword`
      });
    }
    
    // 添加代码片段
    items.push({
      label: 'function',
      kind: CompletionItemKind.Snippet,
      insertText: new SnippetString('function ${1:name}(${2:params}) {\n\t$0\n}'),
      documentation: 'Function declaration snippet'
    });
    
    // 根据上下文添加智能补全
    const lineText = document.lineAt(position.line).text;
    const wordRange = document.getWordRangeAtPosition(position);
    const word = wordRange ? document.getText(wordRange) : '';
    
    if (lineText.includes('import')) {
      items.push(...this.getImportCompletions(document, word));
    }
    
    return items;
  }
  
  resolveCompletionItem(
    item: CompletionItem,
    token: CancellationToken
  ): CompletionItem {
    // 延迟加载详细信息
    if (item.kind === CompletionItemKind.Function) {
      item.documentation = new MarkdownString(
        `**${item.label}**\n\nFunction documentation...`
      );
    }
    
    return item;
  }
  
  private getImportCompletions(document: TextDocument, word: string): CompletionItem[] {
    // 实现导入语句的智能补全
    const imports = ['react', 'lodash', 'axios', 'moment'];
    
    return imports
      .filter(imp => imp.includes(word.toLowerCase()))
      .map(imp => ({
        label: imp,
        kind: CompletionItemKind.Module,
        insertText: imp,
        documentation: `Import ${imp} module`
      }));
  }
}

// 注册补全提供者
const completionDisposable = registerCompletionProvider(
  { language: 'typescript' },
  new CustomCompletionProvider(),
  '.', '(', '['
);

诊断和错误检查

诊断提供者

typescript
// 创建诊断集合
const diagnosticCollection = languages.createDiagnosticCollection('custom-linter');

// 诊断文档
const diagnoseDcoument = (document: TextDocument): void => {
  const diagnostics: Diagnostic[] = [];
  const text = document.getText();
  
  // 检查语法错误
  const syntaxErrors = checkSyntax(text);
  for (const error of syntaxErrors) {
    diagnostics.push({
      range: error.range,
      message: error.message,
      severity: DiagnosticSeverity.Error,
      source: 'custom-linter',
      code: error.code
    });
  }
  
  // 检查代码风格
  const styleWarnings = checkStyle(text);
  for (const warning of styleWarnings) {
    diagnostics.push({
      range: warning.range,
      message: warning.message,
      severity: DiagnosticSeverity.Warning,
      source: 'custom-linter',
      code: warning.code,
      tags: [DiagnosticTag.Unnecessary]
    });
  }
  
  // 设置诊断
  diagnosticCollection.set(document.uri, diagnostics);
};

interface Diagnostic {
  range: Range;
  message: string;
  severity?: DiagnosticSeverity;
  source?: string;
  code?: string | number | { value: string | number; target: Uri };
  relatedInformation?: DiagnosticRelatedInformation[];
  tags?: DiagnosticTag[];
}

enum DiagnosticSeverity {
  Error = 0,
  Warning = 1,
  Information = 2,
  Hint = 3
}

实时诊断

typescript
class LanguageDiagnostics {
  private diagnosticCollection: DiagnosticCollection;
  private disposables: Disposable[] = [];
  
  constructor(languageId: string) {
    this.diagnosticCollection = languages.createDiagnosticCollection(languageId);
    this.setupListeners();
  }
  
  private setupListeners(): void {
    // 监听文档变化
    this.disposables.push(
      workspace.onDidChangeTextDocument(event => {
        if (event.document.languageId === 'typescript') {
          this.validateDocument(event.document);
        }
      })
    );
    
    // 监听文档打开
    this.disposables.push(
      workspace.onDidOpenTextDocument(document => {
        if (document.languageId === 'typescript') {
          this.validateDocument(document);
        }
      })
    );
    
    // 监听文档关闭
    this.disposables.push(
      workspace.onDidCloseTextDocument(document => {
        this.diagnosticCollection.delete(document.uri);
      })
    );
  }
  
  private async validateDocument(document: TextDocument): Promise<void> {
    const diagnostics: Diagnostic[] = [];
    
    try {
      // 异步验证文档
      const errors = await this.analyzeDocument(document);
      
      for (const error of errors) {
        diagnostics.push({
          range: new Range(
            new Position(error.line, error.column),
            new Position(error.line, error.column + error.length)
          ),
          message: error.message,
          severity: this.getSeverity(error.type),
          source: 'typescript-analyzer',
          code: error.code
        });
      }
    } catch (error) {
      console.error('Failed to validate document:', error);
    }
    
    this.diagnosticCollection.set(document.uri, diagnostics);
  }
  
  private async analyzeDocument(document: TextDocument): Promise<AnalysisError[]> {
    // 实现文档分析逻辑
    const text = document.getText();
    const errors: AnalysisError[] = [];
    
    // 示例:检查未使用的变量
    const unusedVariables = this.findUnusedVariables(text);
    errors.push(...unusedVariables);
    
    // 示例:检查类型错误
    const typeErrors = await this.checkTypes(document);
    errors.push(...typeErrors);
    
    return errors;
  }
  
  private getSeverity(type: string): DiagnosticSeverity {
    switch (type) {
      case 'error': return DiagnosticSeverity.Error;
      case 'warning': return DiagnosticSeverity.Warning;
      case 'info': return DiagnosticSeverity.Information;
      default: return DiagnosticSeverity.Hint;
    }
  }
  
  dispose(): void {
    this.diagnosticCollection.dispose();
    this.disposables.forEach(d => d.dispose());
  }
}

interface AnalysisError {
  line: number;
  column: number;
  length: number;
  message: string;
  type: string;
  code: string;
}

符号导航

定义提供者

typescript
// 注册定义提供者
const registerDefinitionProvider = (
  selector: DocumentSelector,
  provider: DefinitionProvider
): Disposable => {
  return languages.registerDefinitionProvider(selector, provider);
};

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

type Definition = Location | Location[];

interface LocationLink {
  originSelectionRange?: Range;
  targetUri: Uri;
  targetRange: Range;
  targetSelectionRange?: Range;
}

// 定义提供者实现
class CustomDefinitionProvider implements DefinitionProvider {
  async provideDefinition(
    document: TextDocument,
    position: Position,
    token: CancellationToken
  ): Promise<Definition | LocationLink[]> {
    const wordRange = document.getWordRangeAtPosition(position);
    if (!wordRange) {
      return [];
    }
    
    const word = document.getText(wordRange);
    
    // 查找符号定义
    const definitions = await this.findDefinitions(document, word);
    
    return definitions.map(def => ({
      originSelectionRange: wordRange,
      targetUri: def.uri,
      targetRange: def.range,
      targetSelectionRange: def.selectionRange
    }));
  }
  
  private async findDefinitions(
    document: TextDocument,
    symbol: string
  ): Promise<SymbolDefinition[]> {
    // 实现符号定义查找逻辑
    const definitions: SymbolDefinition[] = [];
    
    // 在当前文档中查找
    const localDefs = this.findLocalDefinitions(document, symbol);
    definitions.push(...localDefs);
    
    // 在工作区中查找
    const workspaceDefs = await this.findWorkspaceDefinitions(symbol);
    definitions.push(...workspaceDefs);
    
    return definitions;
  }
  
  private findLocalDefinitions(
    document: TextDocument,
    symbol: string
  ): SymbolDefinition[] {
    const definitions: SymbolDefinition[] = [];
    const text = document.getText();
    
    // 使用正则表达式查找定义
    const patterns = [
      new RegExp(`function\\s+${symbol}\\s*\\(`, 'g'),
      new RegExp(`class\\s+${symbol}\\s*{`, 'g'),
      new RegExp(`const\\s+${symbol}\\s*=`, 'g'),
      new RegExp(`let\\s+${symbol}\\s*=`, 'g'),
      new RegExp(`var\\s+${symbol}\\s*=`, 'g')
    ];
    
    for (const pattern of patterns) {
      let match;
      while ((match = pattern.exec(text)) !== null) {
        const position = document.positionAt(match.index);
        const range = new Range(position, position.translate(0, symbol.length));
        
        definitions.push({
          uri: document.uri,
          range,
          selectionRange: range
        });
      }
    }
    
    return definitions;
  }
  
  private async findWorkspaceDefinitions(
    symbol: string
  ): Promise<SymbolDefinition[]> {
    // 在工作区文件中搜索定义
    const files = await workspace.findFiles('**/*.{ts,js}', '**/node_modules/**');
    const definitions: SymbolDefinition[] = [];
    
    for (const file of files) {
      try {
        const document = await workspace.openTextDocument(file);
        const localDefs = this.findLocalDefinitions(document, symbol);
        definitions.push(...localDefs);
      } catch (error) {
        console.warn('Failed to search in file:', file.fsPath);
      }
    }
    
    return definitions;
  }
}

interface SymbolDefinition {
  uri: Uri;
  range: Range;
  selectionRange: Range;
}

引用提供者

typescript
// 注册引用提供者
const registerReferenceProvider = (
  selector: DocumentSelector,
  provider: ReferenceProvider
): Disposable => {
  return languages.registerReferenceProvider(selector, provider);
};

interface ReferenceProvider {
  provideReferences(
    document: TextDocument,
    position: Position,
    context: ReferenceContext,
    token: CancellationToken
  ): ProviderResult<Location[]>;
}

interface ReferenceContext {
  includeDeclaration: boolean;
}

// 引用提供者实现
class CustomReferenceProvider implements ReferenceProvider {
  async provideReferences(
    document: TextDocument,
    position: Position,
    context: ReferenceContext,
    token: CancellationToken
  ): Promise<Location[]> {
    const wordRange = document.getWordRangeAtPosition(position);
    if (!wordRange) {
      return [];
    }
    
    const word = document.getText(wordRange);
    const references: Location[] = [];
    
    // 在工作区中查找所有引用
    const files = await workspace.findFiles('**/*.{ts,js}', '**/node_modules/**');
    
    for (const file of files) {
      if (token.isCancellationRequested) {
        break;
      }
      
      try {
        const doc = await workspace.openTextDocument(file);
        const fileReferences = this.findReferencesInDocument(doc, word);
        references.push(...fileReferences);
      } catch (error) {
        console.warn('Failed to search references in file:', file.fsPath);
      }
    }
    
    return references;
  }
  
  private findReferencesInDocument(
    document: TextDocument,
    symbol: string
  ): Location[] {
    const references: Location[] = [];
    const text = document.getText();
    
    // 使用词边界正则表达式查找精确匹配
    const pattern = new RegExp(`\\b${symbol}\\b`, 'g');
    let match;
    
    while ((match = pattern.exec(text)) !== null) {
      const position = document.positionAt(match.index);
      const range = new Range(
        position,
        position.translate(0, symbol.length)
      );
      
      references.push(new Location(document.uri, range));
    }
    
    return references;
  }
}

代码操作

代码操作提供者

typescript
// 注册代码操作提供者
const registerCodeActionsProvider = (
  selector: DocumentSelector,
  provider: CodeActionProvider,
  metadata?: CodeActionProviderMetadata
): Disposable => {
  return languages.registerCodeActionsProvider(selector, provider, metadata);
};

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

interface CodeAction {
  title: string;
  kind?: CodeActionKind;
  diagnostics?: Diagnostic[];
  isPreferred?: boolean;
  disabled?: { reason: string };
  edit?: WorkspaceEdit;
  command?: Command;
}

// 代码操作提供者实现
class CustomCodeActionProvider implements CodeActionProvider {
  provideCodeActions(
    document: TextDocument,
    range: Range | Selection,
    context: CodeActionContext,
    token: CancellationToken
  ): CodeAction[] {
    const actions: CodeAction[] = [];
    
    // 快速修复操作
    for (const diagnostic of context.diagnostics) {
      if (diagnostic.source === 'custom-linter') {
        actions.push(...this.createQuickFixes(document, diagnostic));
      }
    }
    
    // 重构操作
    const selectedText = document.getText(range);
    if (selectedText.trim()) {
      actions.push(...this.createRefactorActions(document, range, selectedText));
    }
    
    // 源代码操作
    actions.push(...this.createSourceActions(document));
    
    return actions;
  }
  
  private createQuickFixes(
    document: TextDocument,
    diagnostic: Diagnostic
  ): CodeAction[] {
    const actions: CodeAction[] = [];
    
    if (diagnostic.code === 'unused-variable') {
      // 删除未使用的变量
      actions.push({
        title: 'Remove unused variable',
        kind: CodeActionKind.QuickFix,
        diagnostics: [diagnostic],
        edit: {
          changes: {
            [document.uri.toString()]: [
              TextEdit.delete(diagnostic.range)
            ]
          }
        }
      });
    }
    
    if (diagnostic.code === 'missing-semicolon') {
      // 添加分号
      actions.push({
        title: 'Add semicolon',
        kind: CodeActionKind.QuickFix,
        diagnostics: [diagnostic],
        edit: {
          changes: {
            [document.uri.toString()]: [
              TextEdit.insert(diagnostic.range.end, ';')
            ]
          }
        }
      });
    }
    
    return actions;
  }
  
  private createRefactorActions(
    document: TextDocument,
    range: Range,
    selectedText: string
  ): CodeAction[] {
    const actions: CodeAction[] = [];
    
    // 提取为函数
    if (this.canExtractFunction(selectedText)) {
      actions.push({
        title: 'Extract to function',
        kind: CodeActionKind.RefactorExtract,
        edit: this.createExtractFunctionEdit(document, range, selectedText)
      });
    }
    
    // 提取为变量
    if (this.canExtractVariable(selectedText)) {
      actions.push({
        title: 'Extract to variable',
        kind: CodeActionKind.RefactorExtract,
        edit: this.createExtractVariableEdit(document, range, selectedText)
      });
    }
    
    return actions;
  }
  
  private createSourceActions(document: TextDocument): CodeAction[] {
    const actions: CodeAction[] = [];
    
    // 组织导入
    actions.push({
      title: 'Organize imports',
      kind: CodeActionKind.SourceOrganizeImports,
      edit: this.createOrganizeImportsEdit(document)
    });
    
    // 格式化文档
    actions.push({
      title: 'Format document',
      kind: CodeActionKind.SourceFixAll,
      command: {
        title: 'Format document',
        command: 'editor.action.formatDocument'
      }
    });
    
    return actions;
  }
  
  private canExtractFunction(text: string): boolean {
    // 检查是否可以提取为函数
    return text.includes('{') && text.includes('}');
  }
  
  private canExtractVariable(text: string): boolean {
    // 检查是否可以提取为变量
    return !text.includes('\n') && text.length > 5;
  }
  
  private createExtractFunctionEdit(
    document: TextDocument,
    range: Range,
    selectedText: string
  ): WorkspaceEdit {
    const functionName = 'extractedFunction';
    const functionDeclaration = `\nfunction ${functionName}() {\n${selectedText}\n}\n`;
    const functionCall = `${functionName}();`;
    
    const edit = new WorkspaceEdit();
    edit.replace(document.uri, range, functionCall);
    edit.insert(document.uri, new Position(0, 0), functionDeclaration);
    
    return edit;
  }
  
  private createExtractVariableEdit(
    document: TextDocument,
    range: Range,
    selectedText: string
  ): WorkspaceEdit {
    const variableName = 'extractedVariable';
    const variableDeclaration = `const ${variableName} = ${selectedText};\n`;
    
    const edit = new WorkspaceEdit();
    edit.replace(document.uri, range, variableName);
    edit.insert(document.uri, range.start.with(range.start.line, 0), variableDeclaration);
    
    return edit;
  }
  
  private createOrganizeImportsEdit(document: TextDocument): WorkspaceEdit {
    // 实现导入组织逻辑
    const text = document.getText();
    const lines = text.split('\n');
    const imports: string[] = [];
    const otherLines: string[] = [];
    
    for (const line of lines) {
      if (line.trim().startsWith('import ')) {
        imports.push(line);
      } else {
        otherLines.push(line);
      }
    }
    
    // 排序导入
    imports.sort();
    
    const organizedText = [...imports, '', ...otherLines].join('\n');
    
    const edit = new WorkspaceEdit();
    edit.replace(
      document.uri,
      new Range(new Position(0, 0), document.lineAt(document.lineCount - 1).range.end),
      organizedText
    );
    
    return edit;
  }
}

格式化

文档格式化提供者

typescript
// 注册文档格式化提供者
const registerDocumentFormattingEditProvider = (
  selector: DocumentSelector,
  provider: DocumentFormattingEditProvider
): Disposable => {
  return languages.registerDocumentFormattingEditProvider(selector, provider);
};

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

interface FormattingOptions {
  tabSize: number;
  insertSpaces: boolean;
  trimTrailingWhitespace?: boolean;
  insertFinalNewline?: boolean;
  trimFinalNewlines?: boolean;
}

// 格式化提供者实现
class CustomFormattingProvider implements DocumentFormattingEditProvider {
  provideDocumentFormattingEdits(
    document: TextDocument,
    options: FormattingOptions,
    token: CancellationToken
  ): TextEdit[] {
    const edits: TextEdit[] = [];
    const text = document.getText();
    
    // 格式化代码
    const formattedText = this.formatCode(text, options);
    
    if (formattedText !== text) {
      const fullRange = new Range(
        new Position(0, 0),
        document.lineAt(document.lineCount - 1).range.end
      );
      
      edits.push(TextEdit.replace(fullRange, formattedText));
    }
    
    return edits;
  }
  
  private formatCode(text: string, options: FormattingOptions): string {
    let formatted = text;
    
    // 标准化缩进
    formatted = this.normalizeIndentation(formatted, options);
    
    // 格式化括号
    formatted = this.formatBraces(formatted);
    
    // 格式化空格
    formatted = this.formatSpacing(formatted);
    
    // 处理尾随空白
    if (options.trimTrailingWhitespace) {
      formatted = this.trimTrailingWhitespace(formatted);
    }
    
    // 处理最终换行
    if (options.insertFinalNewline && !formatted.endsWith('\n')) {
      formatted += '\n';
    }
    
    return formatted;
  }
  
  private normalizeIndentation(text: string, options: FormattingOptions): string {
    const lines = text.split('\n');
    const indentString = options.insertSpaces ? ' '.repeat(options.tabSize) : '\t';
    
    return lines.map(line => {
      const trimmed = line.trimLeft();
      const indentLevel = this.getIndentLevel(line, trimmed);
      return indentString.repeat(indentLevel) + trimmed;
    }).join('\n');
  }
  
  private getIndentLevel(originalLine: string, trimmedLine: string): number {
    // 简化的缩进级别计算
    const leadingWhitespace = originalLine.length - trimmedLine.length;
    return Math.floor(leadingWhitespace / 2); // 假设每级缩进2个空格
  }
  
  private formatBraces(text: string): string {
    // 格式化大括号
    return text
      .replace(/\{\s*\n/g, '{\n')
      .replace(/\n\s*\}/g, '\n}');
  }
  
  private formatSpacing(text: string): string {
    // 格式化操作符周围的空格
    return text
      .replace(/([=+\-*/%])(?!\s)/g, '$1 ')
      .replace(/(?<!\s)([=+\-*/%])/g, ' $1')
      .replace(/,(?!\s)/g, ', ')
      .replace(/;(?!\s)/g, '; ');
  }
  
  private trimTrailingWhitespace(text: string): string {
    return text.replace(/[ \t]+$/gm, '');
  }
}

语法高亮

语义标记提供者

typescript
// 注册语义标记提供者
const registerDocumentSemanticTokensProvider = (
  selector: DocumentSelector,
  provider: DocumentSemanticTokensProvider,
  legend: SemanticTokensLegend
): Disposable => {
  return languages.registerDocumentSemanticTokensProvider(
    selector,
    provider,
    legend
  );
};

interface DocumentSemanticTokensProvider {
  provideDocumentSemanticTokens(
    document: TextDocument,
    token: CancellationToken
  ): ProviderResult<SemanticTokens>;
}

interface SemanticTokensLegend {
  readonly tokenTypes: string[];
  readonly tokenModifiers: string[];
}

interface SemanticTokens {
  readonly data: Uint32Array;
}

// 语义标记提供者实现
class CustomSemanticTokensProvider implements DocumentSemanticTokensProvider {
  private legend: SemanticTokensLegend;
  
  constructor() {
    this.legend = {
      tokenTypes: [
        'namespace', 'class', 'enum', 'interface', 'struct', 'typeParameter',
        'type', 'parameter', 'variable', 'property', 'enumMember', 'event',
        'function', 'method', 'macro', 'keyword', 'modifier', 'comment',
        'string', 'number', 'regexp', 'operator'
      ],
      tokenModifiers: [
        'declaration', 'definition', 'readonly', 'static', 'deprecated',
        'abstract', 'async', 'modification', 'documentation', 'defaultLibrary'
      ]
    };
  }
  
  provideDocumentSemanticTokens(
    document: TextDocument,
    token: CancellationToken
  ): SemanticTokens {
    const builder = new SemanticTokensBuilder(this.legend);
    const text = document.getText();
    
    // 解析文档并添加语义标记
    this.parseDocument(document, text, builder);
    
    return builder.build();
  }
  
  private parseDocument(
    document: TextDocument,
    text: string,
    builder: SemanticTokensBuilder
  ): void {
    const lines = text.split('\n');
    
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      const line = lines[lineIndex];
      this.parseLine(document, lineIndex, line, builder);
    }
  }
  
  private parseLine(
    document: TextDocument,
    lineIndex: number,
    line: string,
    builder: SemanticTokensBuilder
  ): void {
    // 解析关键字
    this.parseKeywords(lineIndex, line, builder);
    
    // 解析字符串
    this.parseStrings(lineIndex, line, builder);
    
    // 解析数字
    this.parseNumbers(lineIndex, line, builder);
    
    // 解析注释
    this.parseComments(lineIndex, line, builder);
    
    // 解析函数调用
    this.parseFunctionCalls(lineIndex, line, builder);
  }
  
  private parseKeywords(
    lineIndex: number,
    line: string,
    builder: SemanticTokensBuilder
  ): void {
    const keywords = [
      'function', 'class', 'interface', 'type', 'const', 'let', 'var',
      'if', 'else', 'for', 'while', 'return', 'import', 'export'
    ];
    
    for (const keyword of keywords) {
      const regex = new RegExp(`\\b${keyword}\\b`, 'g');
      let match;
      
      while ((match = regex.exec(line)) !== null) {
        builder.push(
          lineIndex,
          match.index,
          keyword.length,
          this.getTokenType('keyword'),
          0
        );
      }
    }
  }
  
  private parseStrings(
    lineIndex: number,
    line: string,
    builder: SemanticTokensBuilder
  ): void {
    const stringRegex = /(["'`])(?:(?!\1)[^\\]|\\.)*\1/g;
    let match;
    
    while ((match = stringRegex.exec(line)) !== null) {
      builder.push(
        lineIndex,
        match.index,
        match[0].length,
        this.getTokenType('string'),
        0
      );
    }
  }
  
  private parseNumbers(
    lineIndex: number,
    line: string,
    builder: SemanticTokensBuilder
  ): void {
    const numberRegex = /\b\d+(\.\d+)?\b/g;
    let match;
    
    while ((match = numberRegex.exec(line)) !== null) {
      builder.push(
        lineIndex,
        match.index,
        match[0].length,
        this.getTokenType('number'),
        0
      );
    }
  }
  
  private parseComments(
    lineIndex: number,
    line: string,
    builder: SemanticTokensBuilder
  ): void {
    // 单行注释
    const singleLineComment = line.indexOf('//');
    if (singleLineComment !== -1) {
      builder.push(
        lineIndex,
        singleLineComment,
        line.length - singleLineComment,
        this.getTokenType('comment'),
        0
      );
    }
  }
  
  private parseFunctionCalls(
    lineIndex: number,
    line: string,
    builder: SemanticTokensBuilder
  ): void {
    const functionCallRegex = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g;
    let match;
    
    while ((match = functionCallRegex.exec(line)) !== null) {
      builder.push(
        lineIndex,
        match.index,
        match[1].length,
        this.getTokenType('function'),
        0
      );
    }
  }
  
  private getTokenType(type: string): number {
    return this.legend.tokenTypes.indexOf(type);
  }
}

class SemanticTokensBuilder {
  private data: number[] = [];
  private prevLine = 0;
  private prevChar = 0;
  
  constructor(private legend: SemanticTokensLegend) {}
  
  push(
    line: number,
    char: number,
    length: number,
    tokenType: number,
    tokenModifiers: number
  ): void {
    const deltaLine = line - this.prevLine;
    const deltaChar = deltaLine === 0 ? char - this.prevChar : char;
    
    this.data.push(deltaLine, deltaChar, length, tokenType, tokenModifiers);
    
    this.prevLine = line;
    this.prevChar = char;
  }
  
  build(): SemanticTokens {
    return {
      data: new Uint32Array(this.data)
    };
  }
}

高级功能

语言服务器协议 (LSP)

typescript
import { LanguageClient, LanguageClientOptions, ServerOptions } from '@trae/language-client';

// 创建语言客户端
const createLanguageClient = (
  serverPath: string,
  languageId: string
): LanguageClient => {
  const serverOptions: ServerOptions = {
    run: { command: serverPath },
    debug: { command: serverPath, args: ['--debug'] }
  };
  
  const clientOptions: LanguageClientOptions = {
    documentSelector: [{ scheme: 'file', language: languageId }],
    synchronize: {
      fileEvents: workspace.createFileSystemWatcher(`**/*.${languageId}`)
    }
  };
  
  return new LanguageClient(
    `${languageId}-language-server`,
    `${languageId} Language Server`,
    serverOptions,
    clientOptions
  );
};

// 启动语言客户端
const startLanguageClient = async (client: LanguageClient): Promise<void> => {
  try {
    await client.start();
    console.log('Language client started successfully');
  } catch (error) {
    console.error('Failed to start language client:', error);
  }
};

自定义语言扩展

typescript
class CustomLanguageExtension {
  private disposables: Disposable[] = [];
  
  activate(): void {
    // 注册语言
    this.disposables.push(
      languages.registerLanguage({
        id: 'custom-lang',
        extensions: ['.custom'],
        aliases: ['Custom Language'],
        mimetypes: ['text/x-custom']
      })
    );
    
    // 设置语言配置
    this.disposables.push(
      languages.setLanguageConfiguration('custom-lang', {
        comments: {
          lineComment: '#',
          blockComment: ['/*', '*/']
        },
        brackets: [['[', ']'], ['(', ')'], ['{', '}']],
        autoClosingPairs: [
          { open: '[', close: ']' },
          { open: '(', close: ')' },
          { open: '{', close: '}' },
          { open: '"', close: '"' }
        ]
      })
    );
    
    // 注册提供者
    this.registerProviders();
  }
  
  private registerProviders(): void {
    const selector = { language: 'custom-lang' };
    
    // 补全提供者
    this.disposables.push(
      languages.registerCompletionItemProvider(
        selector,
        new CustomCompletionProvider(),
        '.', '('
      )
    );
    
    // 定义提供者
    this.disposables.push(
      languages.registerDefinitionProvider(
        selector,
        new CustomDefinitionProvider()
      )
    );
    
    // 引用提供者
    this.disposables.push(
      languages.registerReferenceProvider(
        selector,
        new CustomReferenceProvider()
      )
    );
    
    // 代码操作提供者
    this.disposables.push(
      languages.registerCodeActionsProvider(
        selector,
        new CustomCodeActionProvider()
      )
    );
    
    // 格式化提供者
    this.disposables.push(
      languages.registerDocumentFormattingEditProvider(
        selector,
        new CustomFormattingProvider()
      )
    );
    
    // 语义标记提供者
    this.disposables.push(
      languages.registerDocumentSemanticTokensProvider(
        selector,
        new CustomSemanticTokensProvider(),
        new CustomSemanticTokensProvider().legend
      )
    );
  }
  
  deactivate(): void {
    this.disposables.forEach(d => d.dispose());
    this.disposables = [];
  }
}

相关 API

示例项目

查看 语言服务 API 示例 了解完整的实现示例。

您的终极 AI 驱动 IDE 学习指南