语言服务 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 示例 了解完整的实现示例。