Completion API
Completion APIは、インテリジェントなコード補完機能を提供し、ユーザーが入力する際にコンテキストを認識した提案を提供します。
概要
Completion APIでは以下のことができます:
- インテリジェントなコード補完の提供
- カスタム補完プロバイダーの作成
- 異なる補完トリガーの処理
- 言語サービスとの統合
- スニペット補完のサポート
- ドキュメントと型情報の提供
基本的な使用方法
シンプルな補完プロバイダー
typescript
import { TraeAPI } from '@trae/api';
// 基本的な補完プロバイダーを登録
const provider = TraeAPI.languages.registerCompletionItemProvider(
{ scheme: 'file', language: 'typescript' },
{
async provideCompletionItems(document, position, token, context) {
const lineText = document.lineAt(position.line).text;
const linePrefix = lineText.substring(0, position.character);
// シンプルなキーワード補完
if (linePrefix.endsWith('console.')) {
return [
{
label: 'log',
kind: TraeAPI.CompletionItemKind.Method,
insertText: 'log($1)',
insertTextFormat: TraeAPI.InsertTextFormat.Snippet,
documentation: 'コンソールにメッセージを出力します'
},
{
label: 'error',
kind: TraeAPI.CompletionItemKind.Method,
insertText: 'error($1)',
insertTextFormat: TraeAPI.InsertTextFormat.Snippet,
documentation: 'コンソールにエラーメッセージを出力します'
}
];
}
return [];
}
},
'.', // トリガー文字
' '
);高度な補完プロバイダー
typescript
class AdvancedCompletionProvider implements TraeAPI.CompletionItemProvider {
async provideCompletionItems(
document: TraeAPI.TextDocument,
position: TraeAPI.Position,
token: TraeAPI.CancellationToken,
context: TraeAPI.CompletionContext
): Promise<TraeAPI.CompletionItem[]> {
const completions: TraeAPI.CompletionItem[] = [];
const lineText = document.lineAt(position.line).text;
const wordRange = document.getWordRangeAtPosition(position);
const word = wordRange ? document.getText(wordRange) : '';
// コンテキストを認識した補完
const fileContent = document.getText();
const imports = this.extractImports(fileContent);
const functions = this.extractFunctions(fileContent);
const variables = this.extractVariables(fileContent, position);
// 関数補完を追加
functions.forEach(func => {
completions.push({
label: func.name,
kind: TraeAPI.CompletionItemKind.Function,
insertText: `${func.name}(${func.parameters.map((p, i) => `\${${i + 1}:${p}}`).join(', ')})`,
insertTextFormat: TraeAPI.InsertTextFormat.Snippet,
documentation: func.documentation,
detail: func.signature,
sortText: '1' + func.name // 高い優先度
});
});
// 変数補完を追加
variables.forEach(variable => {
completions.push({
label: variable.name,
kind: TraeAPI.CompletionItemKind.Variable,
insertText: variable.name,
documentation: variable.type,
detail: `${variable.type} ${variable.name}`,
sortText: '2' + variable.name
});
});
// Add import completions
if (lineText.trim().startsWith('import')) {
const availableModules = await this.getAvailableModules();
availableModules.forEach(module => {
completions.push({
label: module.name,
kind: TraeAPI.CompletionItemKind.Module,
insertText: module.name,
documentation: module.description,
detail: module.version
});
});
}
return completions;
}
async resolveCompletionItem(
item: TraeAPI.CompletionItem,
token: TraeAPI.CancellationToken
): Promise<TraeAPI.CompletionItem> {
// Add additional information when item is selected
if (item.kind === TraeAPI.CompletionItemKind.Function) {
const documentation = await this.getFunctionDocumentation(item.label);
item.documentation = {
kind: TraeAPI.MarkupKind.Markdown,
value: documentation
};
}
return item;
}
private extractImports(content: string): Array<{ name: string; path: string }> {
const importRegex = /import\s+.*?from\s+['"]([^'"]+)['"]/g;
const imports: Array<{ name: string; path: string }> = [];
let match;
while ((match = importRegex.exec(content)) !== null) {
imports.push({
name: match[1].split('/').pop() || match[1],
path: match[1]
});
}
return imports;
}
private extractFunctions(content: string): Array<{
name: string;
parameters: string[];
signature: string;
documentation?: string;
}> {
const functionRegex = /(?:function\s+|const\s+|let\s+|var\s+)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*[=:]?\s*(?:function)?\s*\(([^)]*)\)/g;
const functions: Array<{
name: string;
parameters: string[];
signature: string;
documentation?: string;
}> = [];
let match;
while ((match = functionRegex.exec(content)) !== null) {
const name = match[1];
const params = match[2].split(',').map(p => p.trim()).filter(p => p);
functions.push({
name,
parameters: params,
signature: `${name}(${params.join(', ')})`,
documentation: `Function ${name}`
});
}
return functions;
}
private extractVariables(content: string, position: TraeAPI.Position): Array<{
name: string;
type: string;
}> {
const lines = content.split('\n');
const variables: Array<{ name: string; type: string }> = [];
const variableRegex = /(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*:\s*([^=]+))?/g;
// Only look at variables declared before current position
for (let i = 0; i < Math.min(position.line, lines.length); i++) {
let match;
while ((match = variableRegex.exec(lines[i])) !== null) {
variables.push({
name: match[1],
type: match[2] || 'any'
});
}
}
return variables;
}
private async getAvailableModules(): Promise<Array<{
name: string;
description: string;
version: string;
}>> {
// In a real implementation, this would query npm registry or local node_modules
return [
{ name: 'lodash', description: 'A modern JavaScript utility library', version: '4.17.21' },
{ name: 'axios', description: 'Promise based HTTP client', version: '1.6.0' },
{ name: 'react', description: 'A JavaScript library for building user interfaces', version: '18.2.0' }
];
}
private async getFunctionDocumentation(functionName: string): Promise<string> {
// In a real implementation, this would fetch from documentation sources
return `## ${functionName}\n\nDetailed documentation for ${functionName} function.`;
}
}
// Register the advanced provider
const advancedProvider = new AdvancedCompletionProvider();
TraeAPI.languages.registerCompletionItemProvider(
{ scheme: 'file', language: 'typescript' },
advancedProvider,
'.', '(', ' '
);Snippet Completions
Creating Snippet Providers
typescript
class SnippetCompletionProvider implements TraeAPI.CompletionItemProvider {
private snippets = {
'for': {
prefix: 'for',
body: [
'for (let ${1:i} = 0; ${1:i} < ${2:array}.length; ${1:i}++) {',
'\t${3:// code}',
'}'
],
description: 'For loop'
},
'if': {
prefix: 'if',
body: [
'if (${1:condition}) {',
'\t${2:// code}',
'}'
],
description: 'If statement'
},
'func': {
prefix: 'func',
body: [
'function ${1:name}(${2:params}) {',
'\t${3:// code}',
'\treturn ${4:value};',
'}'
],
description: 'Function declaration'
},
'class': {
prefix: 'class',
body: [
'class ${1:ClassName} {',
'\tconstructor(${2:params}) {',
'\t\t${3:// constructor code}',
'\t}',
'',
'\t${4:// methods}',
'}'
],
description: 'Class declaration'
}
};
async provideCompletionItems(
document: TraeAPI.TextDocument,
position: TraeAPI.Position
): Promise<TraeAPI.CompletionItem[]> {
const lineText = document.lineAt(position.line).text;
const linePrefix = lineText.substring(0, position.character);
const word = this.getCurrentWord(linePrefix);
const completions: TraeAPI.CompletionItem[] = [];
// Add snippet completions
Object.entries(this.snippets).forEach(([key, snippet]) => {
if (snippet.prefix.startsWith(word) || word === '') {
completions.push({
label: snippet.prefix,
kind: TraeAPI.CompletionItemKind.Snippet,
insertText: snippet.body.join('\n'),
insertTextFormat: TraeAPI.InsertTextFormat.Snippet,
documentation: snippet.description,
detail: 'Snippet',
sortText: '0' + snippet.prefix // Highest priority
});
}
});
return completions;
}
private getCurrentWord(linePrefix: string): string {
const match = linePrefix.match(/[a-zA-Z_$][a-zA-Z0-9_$]*$/);
return match ? match[0] : '';
}
}
// Register snippet provider
const snippetProvider = new SnippetCompletionProvider();
TraeAPI.languages.registerCompletionItemProvider(
{ scheme: 'file', language: 'typescript' },
snippetProvider
);AI-Powered Completions
Intelligent Code Suggestions
typescript
class AICompletionProvider implements TraeAPI.CompletionItemProvider {
async provideCompletionItems(
document: TraeAPI.TextDocument,
position: TraeAPI.Position,
token: TraeAPI.CancellationToken,
context: TraeAPI.CompletionContext
): Promise<TraeAPI.CompletionItem[]> {
// Get context around cursor
const contextRange = new TraeAPI.Range(
Math.max(0, position.line - 10),
0,
Math.min(document.lineCount - 1, position.line + 10),
0
);
const contextText = document.getText(contextRange);
const currentLine = document.lineAt(position.line).text;
const prefix = currentLine.substring(0, position.character);
// Use AI to generate completions
const aiSuggestions = await TraeAPI.ai.codeGeneration.complete({
document: contextText,
position,
language: document.languageId,
maxSuggestions: 5,
context: {
prefix,
fileName: document.fileName,
projectContext: await this.getProjectContext(document.uri)
}
});
const completions: TraeAPI.CompletionItem[] = [];
aiSuggestions.suggestions.forEach((suggestion, index) => {
completions.push({
label: suggestion.label || suggestion.code.split('\n')[0],
kind: this.getCompletionKind(suggestion.type),
insertText: suggestion.code,
insertTextFormat: suggestion.isSnippet ?
TraeAPI.InsertTextFormat.Snippet :
TraeAPI.InsertTextFormat.PlainText,
documentation: {
kind: TraeAPI.MarkupKind.Markdown,
value: suggestion.explanation || 'AI-generated suggestion'
},
detail: `AI Suggestion (${Math.round(suggestion.confidence * 100)}% confidence)`,
sortText: `ai${index.toString().padStart(2, '0')}`,
filterText: suggestion.filterText || suggestion.code,
additionalTextEdits: suggestion.additionalEdits?.map(edit => ({
range: new TraeAPI.Range(
edit.range.start.line,
edit.range.start.character,
edit.range.end.line,
edit.range.end.character
),
newText: edit.newText
}))
});
});
return completions;
}
private getCompletionKind(type: string): TraeAPI.CompletionItemKind {
switch (type) {
case 'function': return TraeAPI.CompletionItemKind.Function;
case 'method': return TraeAPI.CompletionItemKind.Method;
case 'variable': return TraeAPI.CompletionItemKind.Variable;
case 'class': return TraeAPI.CompletionItemKind.Class;
case 'interface': return TraeAPI.CompletionItemKind.Interface;
case 'module': return TraeAPI.CompletionItemKind.Module;
case 'property': return TraeAPI.CompletionItemKind.Property;
case 'keyword': return TraeAPI.CompletionItemKind.Keyword;
case 'snippet': return TraeAPI.CompletionItemKind.Snippet;
default: return TraeAPI.CompletionItemKind.Text;
}
}
private async getProjectContext(uri: TraeAPI.Uri): Promise<any> {
const workspaceFolder = TraeAPI.workspace.getWorkspaceFolder(uri);
if (!workspaceFolder) {
return {};
}
// Analyze project structure and dependencies
const packageJsonUri = TraeAPI.Uri.joinPath(workspaceFolder.uri, 'package.json');
try {
const packageJsonContent = await TraeAPI.workspace.fs.readFile(packageJsonUri);
const packageJson = JSON.parse(packageJsonContent.toString());
return {
dependencies: packageJson.dependencies || {},
devDependencies: packageJson.devDependencies || {},
scripts: packageJson.scripts || {},
projectType: this.detectProjectType(packageJson)
};
} catch {
return {};
}
}
private detectProjectType(packageJson: any): string {
if (packageJson.dependencies?.react) return 'react';
if (packageJson.dependencies?.vue) return 'vue';
if (packageJson.dependencies?.angular) return 'angular';
if (packageJson.dependencies?.express) return 'express';
if (packageJson.dependencies?.next) return 'nextjs';
return 'javascript';
}
}
// Register AI completion provider
const aiProvider = new AICompletionProvider();
TraeAPI.languages.registerCompletionItemProvider(
{ scheme: 'file' },
aiProvider,
'.', '(', ' ', '='
);Language-Specific Completions
TypeScript Completions
typescript
class TypeScriptCompletionProvider implements TraeAPI.CompletionItemProvider {
async provideCompletionItems(
document: TraeAPI.TextDocument,
position: TraeAPI.Position
): Promise<TraeAPI.CompletionItem[]> {
const completions: TraeAPI.CompletionItem[] = [];
const lineText = document.lineAt(position.line).text;
const linePrefix = lineText.substring(0, position.character);
// Type annotations
if (linePrefix.includes(': ')) {
const typeCompletions = [
'string', 'number', 'boolean', 'object', 'array', 'function',
'Promise', 'Date', 'RegExp', 'Error', 'any', 'unknown', 'never', 'void'
];
typeCompletions.forEach(type => {
completions.push({
label: type,
kind: TraeAPI.CompletionItemKind.TypeParameter,
insertText: type,
documentation: `TypeScript ${type} type`
});
});
}
// Interface completions
if (linePrefix.trim().startsWith('interface ')) {
completions.push({
label: 'interface template',
kind: TraeAPI.CompletionItemKind.Snippet,
insertText: [
'${1:InterfaceName} {',
'\t${2:property}: ${3:type};',
'}'
].join('\n'),
insertTextFormat: TraeAPI.InsertTextFormat.Snippet,
documentation: 'Interface template'
});
}
// Generic completions
if (linePrefix.includes('<')) {
completions.push({
label: 'T',
kind: TraeAPI.CompletionItemKind.TypeParameter,
insertText: 'T',
documentation: 'Generic type parameter'
});
}
return completions;
}
}React Completions
typescript
class ReactCompletionProvider implements TraeAPI.CompletionItemProvider {
private reactSnippets = {
'rfc': {
label: 'React Functional Component',
body: [
'import React from \'react\';',
'',
'interface ${1:ComponentName}Props {',
'\t${2:// props}',
'}',
'',
'const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = (${3:props}) => {',
'\treturn (',
'\t\t<div>',
'\t\t\t${4:// JSX content}',
'\t\t</div>',
'\t);',
'};',
'',
'export default ${1:ComponentName};'
]
},
'useState': {
label: 'useState Hook',
body: [
'const [${1:state}, set${1/(.*)/${1:/capitalize}/}] = useState${2:<${3:type}>}(${4:initialValue});'
]
},
'useEffect': {
label: 'useEffect Hook',
body: [
'useEffect(() => {',
'\t${1:// effect}',
'\treturn () => {',
'\t\t${2:// cleanup}',
'\t};',
'}, [${3:dependencies}]);'
]
}
};
async provideCompletionItems(
document: TraeAPI.TextDocument,
position: TraeAPI.Position
): Promise<TraeAPI.CompletionItem[]> {
const completions: TraeAPI.CompletionItem[] = [];
const lineText = document.lineAt(position.line).text;
const linePrefix = lineText.substring(0, position.character);
// Check if we're in a React file
const isReactFile = document.getText().includes('import React') ||
document.fileName.includes('.jsx') ||
document.fileName.includes('.tsx');
if (!isReactFile) {
return completions;
}
// Add React snippets
Object.entries(this.reactSnippets).forEach(([key, snippet]) => {
if (key.startsWith(linePrefix.trim()) || linePrefix.trim() === '') {
completions.push({
label: key,
kind: TraeAPI.CompletionItemKind.Snippet,
insertText: snippet.body.join('\n'),
insertTextFormat: TraeAPI.InsertTextFormat.Snippet,
documentation: snippet.label,
detail: 'React Snippet'
});
}
});
// JSX attribute completions
if (linePrefix.includes('<') && !linePrefix.includes('>')) {
const commonProps = [
'className', 'style', 'onClick', 'onChange', 'onSubmit',
'id', 'key', 'ref', 'children', 'disabled', 'placeholder'
];
commonProps.forEach(prop => {
completions.push({
label: prop,
kind: TraeAPI.CompletionItemKind.Property,
insertText: `${prop}={$1}`,
insertTextFormat: TraeAPI.InsertTextFormat.Snippet,
documentation: `React ${prop} prop`
});
});
}
return completions;
}
}Completion Configuration
Provider Settings
typescript
interface CompletionConfiguration {
enabled: boolean;
triggerCharacters: string[];
maxSuggestions: number;
sortOrder: 'alphabetical' | 'relevance' | 'frequency';
includeSnippets: boolean;
includeAISuggestions: boolean;
aiConfidenceThreshold: number;
cacheResults: boolean;
cacheDuration: number;
}
class CompletionManager {
private config: CompletionConfiguration;
private providers: Map<string, TraeAPI.CompletionItemProvider> = new Map();
private cache: Map<string, { items: TraeAPI.CompletionItem[]; timestamp: number }> = new Map();
constructor(config: CompletionConfiguration) {
this.config = config;
this.setupProviders();
}
private setupProviders() {
// Register providers based on configuration
if (this.config.includeSnippets) {
this.registerProvider('snippets', new SnippetCompletionProvider());
}
if (this.config.includeAISuggestions) {
this.registerProvider('ai', new AICompletionProvider());
}
// Language-specific providers
this.registerProvider('typescript', new TypeScriptCompletionProvider());
this.registerProvider('react', new ReactCompletionProvider());
}
registerProvider(name: string, provider: TraeAPI.CompletionItemProvider) {
this.providers.set(name, provider);
TraeAPI.languages.registerCompletionItemProvider(
{ scheme: 'file' },
provider,
...this.config.triggerCharacters
);
}
async getCompletions(
document: TraeAPI.TextDocument,
position: TraeAPI.Position
): Promise<TraeAPI.CompletionItem[]> {
if (!this.config.enabled) {
return [];
}
const cacheKey = `${document.uri.toString()}:${position.line}:${position.character}`;
// Check cache
if (this.config.cacheResults) {
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.config.cacheDuration) {
return cached.items;
}
}
// Collect completions from all providers
const allCompletions: TraeAPI.CompletionItem[] = [];
for (const [name, provider] of this.providers) {
try {
const items = await provider.provideCompletionItems(
document,
position,
new TraeAPI.CancellationTokenSource().token,
{ triggerKind: TraeAPI.CompletionTriggerKind.Invoke }
);
if (items) {
allCompletions.push(...(Array.isArray(items) ? items : items.items));
}
} catch (error) {
console.error(`Error in completion provider ${name}:`, error);
}
}
// Filter and sort completions
let filteredCompletions = allCompletions;
// Filter by AI confidence threshold
if (this.config.includeAISuggestions) {
filteredCompletions = filteredCompletions.filter(item => {
if (item.detail?.includes('AI Suggestion')) {
const confidenceMatch = item.detail.match(/(\d+)% confidence/);
if (confidenceMatch) {
const confidence = parseInt(confidenceMatch[1]);
return confidence >= this.config.aiConfidenceThreshold;
}
}
return true;
});
}
// Limit number of suggestions
if (filteredCompletions.length > this.config.maxSuggestions) {
filteredCompletions = filteredCompletions.slice(0, this.config.maxSuggestions);
}
// Sort completions
this.sortCompletions(filteredCompletions);
// Cache results
if (this.config.cacheResults) {
this.cache.set(cacheKey, {
items: filteredCompletions,
timestamp: Date.now()
});
}
return filteredCompletions;
}
private sortCompletions(completions: TraeAPI.CompletionItem[]) {
switch (this.config.sortOrder) {
case 'alphabetical':
completions.sort((a, b) => a.label.localeCompare(b.label));
break;
case 'relevance':
completions.sort((a, b) => (a.sortText || a.label).localeCompare(b.sortText || b.label));
break;
case 'frequency':
// Would need usage statistics
break;
}
}
updateConfiguration(newConfig: Partial<CompletionConfiguration>) {
this.config = { ...this.config, ...newConfig };
this.cache.clear(); // Clear cache when config changes
}
}
// Initialize completion manager
const completionManager = new CompletionManager({
enabled: true,
triggerCharacters: ['.', '(', ' ', '=', '<', ':', '"', "'"],
maxSuggestions: 20,
sortOrder: 'relevance',
includeSnippets: true,
includeAISuggestions: true,
aiConfidenceThreshold: 70,
cacheResults: true,
cacheDuration: 5000 // 5 seconds
});API Reference
Core Interfaces
typescript
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;
kind?: CompletionItemKind;
detail?: string;
documentation?: string | MarkupContent;
sortText?: string;
filterText?: string;
insertText?: string;
insertTextFormat?: InsertTextFormat;
range?: Range;
commitCharacters?: string[];
additionalTextEdits?: TextEdit[];
command?: Command;
}
enum CompletionItemKind {
Text = 0,
Method = 1,
Function = 2,
Constructor = 3,
Field = 4,
Variable = 5,
Class = 6,
Interface = 7,
Module = 8,
Property = 9,
Unit = 10,
Value = 11,
Enum = 12,
Keyword = 13,
Snippet = 14,
Color = 15,
File = 16,
Reference = 17,
Folder = 18,
EnumMember = 19,
Constant = 20,
Struct = 21,
Event = 22,
Operator = 23,
TypeParameter = 24
}Best Practices
- Performance: Keep completion providers fast and responsive
- Relevance: Provide contextually relevant suggestions
- Caching: Cache expensive computations and API calls
- Error Handling: Handle errors gracefully without breaking the completion flow
- User Experience: Provide clear labels and helpful documentation
- Filtering: Implement proper filtering based on user input
- Sorting: Sort suggestions by relevance and frequency
- Debouncing: Debounce rapid completion requests
Related APIs
- Editor API - For editor integration
- AI Chat API - For conversational assistance