Skip to content

语言服务 API

Trae IDE 的语言服务 API 提供了丰富的语言功能支持,包括智能感知、代码补全、语法高亮、错误检查等。

概述

语言服务功能包括:

  • 代码补全和智能感知
  • 语法高亮和语义着色
  • 错误检查和诊断
  • 代码导航和引用查找
  • 重构和代码操作
  • 悬停信息和文档

主要接口

CompletionItemProvider

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

HoverProvider

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

DefinitionProvider

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

注册语言服务

基本注册

typescript
import { languages, CompletionItemKind, SnippetString } from 'trae-api'

// 注册代码补全提供者
const completionProvider = languages.registerCompletionItemProvider(
  'javascript',  // 语言标识符
  {
    provideCompletionItems(document, position, token, context) {
      // 提供补全项
      const completions = [
        {
          label: 'console.log',
          kind: CompletionItemKind.Function,
          insertText: new SnippetString('console.log(${1:message})'),
          documentation: '在控制台输出信息'
        }
      ]
      
      return completions
    }
  },
  '.'  // 触发字符
)

// 注册悬停提供者
const hoverProvider = languages.registerHoverProvider('javascript', {
  provideHover(document, position, token) {
    const range = document.getWordRangeAtPosition(position)
    const word = document.getText(range)
    
    if (word === 'console') {
      return {
        contents: ['**Console API**', '用于调试和日志输出的全局对象']
      }
    }
  }
})

多语言支持

typescript
// 支持多种语言
const selector: DocumentSelector = [
  { scheme: 'file', language: 'javascript' },
  { scheme: 'file', language: 'typescript' },
  { scheme: 'file', language: 'javascriptreact' },
  { scheme: 'file', language: 'typescriptreact' }
]

const provider = languages.registerCompletionItemProvider(
  selector,
  new MyCompletionProvider(),
  '.', '(', ':'
)

代码补全

基础补全提供者

typescript
class JavaScriptCompletionProvider implements CompletionItemProvider {
  provideCompletionItems(
    document: TextDocument,
    position: Position,
    token: CancellationToken,
    context: CompletionContext
  ): CompletionItem[] {
    const completions: CompletionItem[] = []
    
    // 获取当前行文本
    const lineText = document.lineAt(position).text
    const linePrefix = lineText.substring(0, position.character)
    
    // 根据上下文提供不同的补全
    if (linePrefix.endsWith('console.')) {
      completions.push(...this.getConsoleCompletions())
    } else if (linePrefix.includes('function ')) {
      completions.push(...this.getFunctionCompletions())
    } else {
      completions.push(...this.getGeneralCompletions())
    }
    
    return completions
  }
  
  private getConsoleCompletions(): CompletionItem[] {
    return [
      {
        label: 'log',
        kind: CompletionItemKind.Method,
        insertText: new SnippetString('log(${1:message})'),
        documentation: '输出日志信息',
        detail: 'console.log(message: any): void'
      },
      {
        label: 'error',
        kind: CompletionItemKind.Method,
        insertText: new SnippetString('error(${1:message})'),
        documentation: '输出错误信息',
        detail: 'console.error(message: any): void'
      },
      {
        label: 'warn',
        kind: CompletionItemKind.Method,
        insertText: new SnippetString('warn(${1:message})'),
        documentation: '输出警告信息',
        detail: 'console.warn(message: any): void'
      }
    ]
  }
  
  private getFunctionCompletions(): CompletionItem[] {
    return [
      {
        label: 'async function',
        kind: CompletionItemKind.Snippet,
        insertText: new SnippetString('async function ${1:name}(${2:params}) {\n\t${3:// TODO: implement}\n}'),
        documentation: '创建异步函数',
        sortText: '0'  // 优先显示
      }
    ]
  }
  
  private getGeneralCompletions(): CompletionItem[] {
    return [
      {
        label: 'if',
        kind: CompletionItemKind.Keyword,
        insertText: new SnippetString('if (${1:condition}) {\n\t${2:// TODO}\n}'),
        documentation: '条件语句'
      },
      {
        label: 'for',
        kind: CompletionItemKind.Keyword,
        insertText: new SnippetString('for (let ${1:i} = 0; ${1:i} < ${2:length}; ${1:i}++) {\n\t${3:// TODO}\n}'),
        documentation: '循环语句'
      }
    ]
  }
  
  resolveCompletionItem(item: CompletionItem, token: CancellationToken): CompletionItem {
    // 延迟加载详细信息
    if (item.label === 'log') {
      item.documentation = new MarkdownString(`
**console.log()**

输出信息到控制台。

\`\`\`javascript
console.log('Hello, World!')
console.log('Value:', value)
\`\`\`
      `)
    }
    
    return item
  }
}

智能补全

typescript
class IntelligentCompletionProvider implements CompletionItemProvider {
  private symbolCache = new Map<string, CompletionItem[]>()
  
  async provideCompletionItems(
    document: TextDocument,
    position: Position,
    token: CancellationToken,
    context: CompletionContext
  ): Promise<CompletionItem[]> {
    const completions: CompletionItem[] = []
    
    // 分析当前上下文
    const analysis = await this.analyzeContext(document, position)
    
    // 根据上下文类型提供补全
    switch (analysis.type) {
      case 'property-access':
        completions.push(...await this.getPropertyCompletions(analysis.object))
        break
        
      case 'function-call':
        completions.push(...await this.getFunctionCompletions(analysis.function))
        break
        
      case 'import-statement':
        completions.push(...await this.getImportCompletions(analysis.module))
        break
        
      default:
        completions.push(...await this.getContextualCompletions(document, position))
    }
    
    return completions
  }
  
  private async analyzeContext(document: TextDocument, position: Position) {
    const lineText = document.lineAt(position).text
    const beforeCursor = lineText.substring(0, position.character)
    
    // 属性访问
    const propertyMatch = beforeCursor.match(/(\w+)\.(\w*)$/)
    if (propertyMatch) {
      return {
        type: 'property-access',
        object: propertyMatch[1],
        property: propertyMatch[2]
      }
    }
    
    // 函数调用
    const functionMatch = beforeCursor.match(/(\w+)\(\s*$/)
    if (functionMatch) {
      return {
        type: 'function-call',
        function: functionMatch[1]
      }
    }
    
    // 导入语句
    const importMatch = beforeCursor.match(/import\s+.*from\s+['"]([^'"]*$)/)
    if (importMatch) {
      return {
        type: 'import-statement',
        module: importMatch[1]
      }
    }
    
    return { type: 'general' }
  }
  
  private async getPropertyCompletions(objectName: string): Promise<CompletionItem[]> {
    // 根据对象类型返回属性补全
    const objectType = await this.inferObjectType(objectName)
    
    switch (objectType) {
      case 'Array':
        return this.getArrayMethods()
      case 'String':
        return this.getStringMethods()
      case 'Object':
        return this.getObjectMethods()
      default:
        return []
    }
  }
  
  private getArrayMethods(): CompletionItem[] {
    return [
      {
        label: 'push',
        kind: CompletionItemKind.Method,
        insertText: new SnippetString('push(${1:element})'),
        documentation: '向数组末尾添加元素',
        detail: 'push(...items: T[]): number'
      },
      {
        label: 'map',
        kind: CompletionItemKind.Method,
        insertText: new SnippetString('map(${1:item} => ${2:item})'),
        documentation: '创建一个新数组,包含调用函数的结果',
        detail: 'map<U>(callbackfn: (value: T) => U): U[]'
      },
      {
        label: 'filter',
        kind: CompletionItemKind.Method,
        insertText: new SnippetString('filter(${1:item} => ${2:condition})'),
        documentation: '创建一个新数组,包含通过测试的元素',
        detail: 'filter(predicate: (value: T) => boolean): T[]'
      }
    ]
  }
  
  private async inferObjectType(objectName: string): Promise<string> {
    // 简单的类型推断逻辑
    if (objectName.includes('array') || objectName.includes('list')) {
      return 'Array'
    }
    if (objectName.includes('string') || objectName.includes('text')) {
      return 'String'
    }
    return 'Object'
  }
}

悬停信息

悬停提供者

typescript
class DocumentationHoverProvider implements HoverProvider {
  private documentationCache = new Map<string, MarkdownString>()
  
  async provideHover(
    document: TextDocument,
    position: Position,
    token: CancellationToken
  ): Promise<Hover | undefined> {
    const range = document.getWordRangeAtPosition(position)
    if (!range) {
      return undefined
    }
    
    const word = document.getText(range)
    const documentation = await this.getDocumentation(word, document, position)
    
    if (documentation) {
      return new Hover(documentation, range)
    }
    
    return undefined
  }
  
  private async getDocumentation(
    word: string,
    document: TextDocument,
    position: Position
  ): Promise<MarkdownString | undefined> {
    // 检查缓存
    if (this.documentationCache.has(word)) {
      return this.documentationCache.get(word)
    }
    
    // 分析符号类型
    const symbolInfo = await this.analyzeSymbol(word, document, position)
    
    if (symbolInfo) {
      const markdown = this.createDocumentation(symbolInfo)
      this.documentationCache.set(word, markdown)
      return markdown
    }
    
    return undefined
  }
  
  private async analyzeSymbol(
    word: string,
    document: TextDocument,
    position: Position
  ) {
    // 获取符号定义
    const definition = await this.findDefinition(word, document)
    
    if (definition) {
      return {
        name: word,
        type: definition.type,
        description: definition.description,
        signature: definition.signature,
        examples: definition.examples
      }
    }
    
    // 检查是否是内置 API
    const builtinInfo = this.getBuiltinInfo(word)
    if (builtinInfo) {
      return builtinInfo
    }
    
    return null
  }
  
  private createDocumentation(symbolInfo: any): MarkdownString {
    const markdown = new MarkdownString()
    
    // 添加签名
    if (symbolInfo.signature) {
      markdown.appendCodeblock(symbolInfo.signature, 'javascript')
    }
    
    // 添加描述
    if (symbolInfo.description) {
      markdown.appendMarkdown(`\n${symbolInfo.description}\n`)
    }
    
    // 添加示例
    if (symbolInfo.examples && symbolInfo.examples.length > 0) {
      markdown.appendMarkdown('\n**示例:**\n')
      symbolInfo.examples.forEach((example: string) => {
        markdown.appendCodeblock(example, 'javascript')
      })
    }
    
    // 添加链接
    if (symbolInfo.documentation) {
      markdown.appendMarkdown(`\n[查看文档](${symbolInfo.documentation})`)
    }
    
    return markdown
  }
  
  private getBuiltinInfo(word: string) {
    const builtins: Record<string, any> = {
      'console': {
        name: 'console',
        type: 'object',
        description: '控制台对象,用于调试和日志输出',
        signature: 'console: Console',
        examples: [
          'console.log("Hello, World!")',
          'console.error("Something went wrong")'
        ]
      },
      'setTimeout': {
        name: 'setTimeout',
        type: 'function',
        description: '在指定的延迟后执行函数',
        signature: 'setTimeout(callback: Function, delay: number): number',
        examples: [
          'setTimeout(() => console.log("Hello"), 1000)'
        ]
      }
    }
    
    return builtins[word]
  }
  
  private async findDefinition(word: string, document: TextDocument) {
    // 简单的定义查找逻辑
    const text = document.getText()
    const functionRegex = new RegExp(`function\\s+${word}\\s*\\([^)]*\\)`, 'g')
    const variableRegex = new RegExp(`(?:let|const|var)\\s+${word}\\s*=`, 'g')
    
    if (functionRegex.test(text)) {
      return {
        type: 'function',
        description: `用户定义的函数 ${word}`,
        signature: `function ${word}(...)`
      }
    }
    
    if (variableRegex.test(text)) {
      return {
        type: 'variable',
        description: `用户定义的变量 ${word}`,
        signature: `${word}: any`
      }
    }
    
    return null
  }
}

定义和引用

定义提供者

typescript
class DefinitionProvider implements vscode.DefinitionProvider {
  async provideDefinition(
    document: TextDocument,
    position: Position,
    token: CancellationToken
  ): Promise<Definition | undefined> {
    const range = document.getWordRangeAtPosition(position)
    if (!range) {
      return undefined
    }
    
    const word = document.getText(range)
    const definitions = await this.findDefinitions(word, document)
    
    return definitions
  }
  
  private async findDefinitions(word: string, document: TextDocument): Promise<Location[]> {
    const definitions: Location[] = []
    const text = document.getText()
    const lines = text.split('\n')
    
    // 查找函数定义
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i]
      const functionMatch = line.match(new RegExp(`function\\s+${word}\\s*\\(`))
      
      if (functionMatch) {
        const position = new Position(i, functionMatch.index!)
        definitions.push(new Location(document.uri, position))
      }
      
      // 查找变量定义
      const variableMatch = line.match(new RegExp(`(?:let|const|var)\\s+${word}\\s*=`))
      if (variableMatch) {
        const position = new Position(i, variableMatch.index!)
        definitions.push(new Location(document.uri, position))
      }
    }
    
    return definitions
  }
}

引用提供者

typescript
class ReferenceProvider implements vscode.ReferenceProvider {
  async provideReferences(
    document: TextDocument,
    position: Position,
    context: ReferenceContext,
    token: CancellationToken
  ): Promise<Location[]> {
    const range = document.getWordRangeAtPosition(position)
    if (!range) {
      return []
    }
    
    const word = document.getText(range)
    const references = await this.findReferences(word, document, context.includeDeclaration)
    
    return references
  }
  
  private async findReferences(
    word: string,
    document: TextDocument,
    includeDeclaration: boolean
  ): Promise<Location[]> {
    const references: Location[] = []
    const text = document.getText()
    const lines = text.split('\n')
    
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i]
      const regex = new RegExp(`\\b${word}\\b`, 'g')
      let match
      
      while ((match = regex.exec(line)) !== null) {
        const position = new Position(i, match.index)
        
        // 检查是否是定义
        const isDefinition = this.isDefinition(line, match.index, word)
        
        if (includeDeclaration || !isDefinition) {
          references.push(new Location(document.uri, position))
        }
      }
    }
    
    return references
  }
  
  private isDefinition(line: string, index: number, word: string): boolean {
    const beforeWord = line.substring(0, index)
    
    // 检查是否是函数定义
    if (beforeWord.match(/function\s*$/)) {
      return true
    }
    
    // 检查是否是变量定义
    if (beforeWord.match(/(?:let|const|var)\s*$/)) {
      return true
    }
    
    return false
  }
}

实用示例

TypeScript 语言服务

typescript
class TypeScriptLanguageService {
  private completionProvider: Disposable
  private hoverProvider: Disposable
  private definitionProvider: Disposable
  private diagnosticCollection: DiagnosticCollection
  
  constructor() {
    this.setupProviders()
    this.diagnosticCollection = languages.createDiagnosticCollection('typescript')
  }
  
  private setupProviders() {
    const selector = { scheme: 'file', language: 'typescript' }
    
    // 注册补全提供者
    this.completionProvider = languages.registerCompletionItemProvider(
      selector,
      new TypeScriptCompletionProvider(),
      '.', '(', '<'
    )
    
    // 注册悬停提供者
    this.hoverProvider = languages.registerHoverProvider(
      selector,
      new TypeScriptHoverProvider()
    )
    
    // 注册定义提供者
    this.definitionProvider = languages.registerDefinitionProvider(
      selector,
      new TypeScriptDefinitionProvider()
    )
    
    // 监听文档变化进行诊断
    workspace.onDidChangeTextDocument(event => {
      if (event.document.languageId === 'typescript') {
        this.validateDocument(event.document)
      }
    })
  }
  
  private async validateDocument(document: TextDocument) {
    const diagnostics = await this.getDiagnostics(document)
    this.diagnosticCollection.set(document.uri, diagnostics)
  }
  
  private async getDiagnostics(document: TextDocument): Promise<Diagnostic[]> {
    // 简单的 TypeScript 语法检查
    const diagnostics: Diagnostic[] = []
    const text = document.getText()
    const lines = text.split('\n')
    
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i]
      
      // 检查未声明的变量
      const undeclaredMatch = line.match(/\b(\w+)\s*=/)
      if (undeclaredMatch && !line.includes('let') && !line.includes('const') && !line.includes('var')) {
        const diagnostic = new Diagnostic(
          new Range(i, undeclaredMatch.index!, i, undeclaredMatch.index! + undeclaredMatch[1].length),
          `变量 '${undeclaredMatch[1]}' 未声明`,
          DiagnosticSeverity.Error
        )
        diagnostic.source = 'TypeScript'
        diagnostics.push(diagnostic)
      }
    }
    
    return diagnostics
  }
  
  dispose() {
    this.completionProvider.dispose()
    this.hoverProvider.dispose()
    this.definitionProvider.dispose()
    this.diagnosticCollection.dispose()
  }
}

最佳实践

  1. 性能优化: 使用缓存和延迟加载减少计算开销
  2. 用户体验: 提供准确和有用的补全建议
  3. 错误处理: 妥善处理语言分析中的异常情况
  4. 扩展性: 设计可扩展的架构支持多种语言特性
  5. 一致性: 保持与 IDE 其他语言服务的一致性

相关 API

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