307 lines
13 KiB
TypeScript
307 lines
13 KiB
TypeScript
|
|
import * as vscode from 'vscode';
|
||
|
|
import { FunctionCacheManager, FunctionInfo } from '../functionExtractor';
|
||
|
|
import { ApiParser, ApiFunction, ApiClass, ApiConstant } from './apiParser';
|
||
|
|
|
||
|
|
// 自动完成项类型枚举
|
||
|
|
export enum CompletionItemType {
|
||
|
|
Function = 'function',
|
||
|
|
Variable = 'variable',
|
||
|
|
Keyword = 'keyword',
|
||
|
|
Constant = 'constant',
|
||
|
|
Class = 'class',
|
||
|
|
Property = 'property',
|
||
|
|
Method = 'method'
|
||
|
|
}
|
||
|
|
|
||
|
|
// 自动完成项接口
|
||
|
|
export interface CompletionItem {
|
||
|
|
label: string;
|
||
|
|
kind: vscode.CompletionItemKind;
|
||
|
|
detail?: string;
|
||
|
|
documentation?: string;
|
||
|
|
insertText?: string;
|
||
|
|
filePath?: string; // 文件路径,用于跨文件函数
|
||
|
|
}
|
||
|
|
|
||
|
|
// 基础关键字列表
|
||
|
|
const KEYWORDS = [
|
||
|
|
'if', 'else', 'while', 'for', 'foreach', 'do', 'switch', 'case', 'default',
|
||
|
|
'break', 'continue', 'return', 'yield', 'try', 'catch', 'throw', 'resume',
|
||
|
|
'function', 'local', 'let', 'const', 'static', 'enum', 'class', 'extends',
|
||
|
|
'constructor', 'typeof', 'instanceof', 'in', 'delete', 'delegate', 'vargc',
|
||
|
|
'vargv', 'tailcall', 'clone', 'weakref', 'null', 'true', 'false'
|
||
|
|
];
|
||
|
|
|
||
|
|
// 常量列表
|
||
|
|
const CONSTANTS = [
|
||
|
|
'PI', 'E', 'RAND_MAX', 'CHAR_BIT', 'CHAR_MAX', 'CHAR_MIN', 'SHRT_MAX',
|
||
|
|
'SHRT_MIN', 'INT_MAX', 'INT_MIN', 'LONG_MAX', 'LONG_MIN', 'FLT_MAX',
|
||
|
|
'FLT_MIN', 'DBL_MAX', 'DBL_MIN'
|
||
|
|
];
|
||
|
|
|
||
|
|
// 基础自动完成提供者类
|
||
|
|
export class CompletionProvider implements vscode.CompletionItemProvider {
|
||
|
|
private cacheManager: FunctionCacheManager;
|
||
|
|
private apiParser: ApiParser;
|
||
|
|
|
||
|
|
constructor(cacheManager: FunctionCacheManager) {
|
||
|
|
this.cacheManager = cacheManager;
|
||
|
|
this.apiParser = ApiParser.getInstance();
|
||
|
|
}
|
||
|
|
|
||
|
|
// 提供自动完成项目列表
|
||
|
|
async provideCompletionItems(
|
||
|
|
document: vscode.TextDocument,
|
||
|
|
position: vscode.Position,
|
||
|
|
token: vscode.CancellationToken,
|
||
|
|
context: vscode.CompletionContext
|
||
|
|
): Promise<vscode.CompletionItem[]> {
|
||
|
|
const completions: vscode.CompletionItem[] = [];
|
||
|
|
|
||
|
|
// 添加关键字完成项
|
||
|
|
completions.push(...this.getKeywordCompletions());
|
||
|
|
|
||
|
|
// 添加API函数完成项
|
||
|
|
completions.push(...this.getApiFunctionCompletions());
|
||
|
|
|
||
|
|
// 添加API类完成项
|
||
|
|
completions.push(...this.getApiClassCompletions());
|
||
|
|
|
||
|
|
// 添加API常量完成项
|
||
|
|
completions.push(...this.getApiConstantCompletions());
|
||
|
|
|
||
|
|
// 添加常量完成项
|
||
|
|
completions.push(...this.getConstantCompletions());
|
||
|
|
|
||
|
|
// 添加跨文件函数完成项
|
||
|
|
completions.push(...await this.getCrossFileFunctionCompletions());
|
||
|
|
|
||
|
|
// 添加当前文档中的变量完成项
|
||
|
|
completions.push(...this.getDocumentVariableCompletions(document, position));
|
||
|
|
|
||
|
|
return completions;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取关键字完成项
|
||
|
|
private getKeywordCompletions(): vscode.CompletionItem[] {
|
||
|
|
return KEYWORDS.map(keyword => {
|
||
|
|
const item = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||
|
|
item.detail = 'Squirrel 关键字';
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取API函数完成项
|
||
|
|
private getApiFunctionCompletions(): vscode.CompletionItem[] {
|
||
|
|
const functions = this.apiParser.getFunctions();
|
||
|
|
return functions.map(func => {
|
||
|
|
const item = new vscode.CompletionItem(func.name, vscode.CompletionItemKind.Function);
|
||
|
|
item.detail = '内置函数';
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${this.apiParser.generateFunctionSignature(func)}\n\`\`\`\n${func.description}`);
|
||
|
|
item.insertText = func.name;
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取API类完成项
|
||
|
|
private getApiClassCompletions(): vscode.CompletionItem[] {
|
||
|
|
const classes = this.apiParser.getClasses();
|
||
|
|
return classes.map(cls => {
|
||
|
|
const item = new vscode.CompletionItem(cls.name, vscode.CompletionItemKind.Class);
|
||
|
|
item.detail = '内置类';
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${this.apiParser.generateClassSignature(cls)}\n\`\`\`\n${cls.description}`);
|
||
|
|
item.insertText = cls.name;
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取API常量完成项
|
||
|
|
private getApiConstantCompletions(): vscode.CompletionItem[] {
|
||
|
|
const constants = this.apiParser.getConstants();
|
||
|
|
return constants.map(constant => {
|
||
|
|
const item = new vscode.CompletionItem(constant.name, vscode.CompletionItemKind.Constant);
|
||
|
|
item.detail = `常量 (${constant.category})`;
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${constant.name} = ${constant.value}\n\`\`\`\n${constant.description}`);
|
||
|
|
item.insertText = constant.name;
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取常量完成项
|
||
|
|
private getConstantCompletions(): vscode.CompletionItem[] {
|
||
|
|
return CONSTANTS.map(constant => {
|
||
|
|
const item = new vscode.CompletionItem(constant, vscode.CompletionItemKind.Constant);
|
||
|
|
item.detail = 'Squirrel 常量';
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取跨文件函数完成项
|
||
|
|
private async getCrossFileFunctionCompletions(): Promise<vscode.CompletionItem[]> {
|
||
|
|
const completions: vscode.CompletionItem[] = [];
|
||
|
|
const allFunctions = this.cacheManager.getAllFunctions();
|
||
|
|
|
||
|
|
// 按函数名分组,处理重复函数名的情况
|
||
|
|
const functionMap = new Map<string, FunctionInfo[]>();
|
||
|
|
allFunctions.forEach(func => {
|
||
|
|
if (!functionMap.has(func.name)) {
|
||
|
|
functionMap.set(func.name, []);
|
||
|
|
}
|
||
|
|
functionMap.get(func.name)!.push(func);
|
||
|
|
});
|
||
|
|
|
||
|
|
// 为每个函数名创建完成项
|
||
|
|
for (const [functionName, functions] of functionMap.entries()) {
|
||
|
|
if (functions.length === 1) {
|
||
|
|
// 单个函数
|
||
|
|
const func = functions[0];
|
||
|
|
const item = new vscode.CompletionItem(functionName, vscode.CompletionItemKind.Function);
|
||
|
|
item.detail = `函数 - ${func.filePath}`;
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${func.signature}\n\`\`\``);
|
||
|
|
item.insertText = functionName;
|
||
|
|
completions.push(item);
|
||
|
|
} else {
|
||
|
|
// 多个同名函数,创建带文件路径信息的完成项
|
||
|
|
functions.forEach(func => {
|
||
|
|
const item = new vscode.CompletionItem(`${functionName} (${func.filePath})`, vscode.CompletionItemKind.Function);
|
||
|
|
item.detail = `函数 - ${func.filePath}`;
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${func.signature}\n\`\`\``);
|
||
|
|
item.insertText = functionName;
|
||
|
|
item.filterText = functionName;
|
||
|
|
completions.push(item);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return completions;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取文档中的变量完成项
|
||
|
|
private getDocumentVariableCompletions(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] {
|
||
|
|
const completions: vscode.CompletionItem[] = [];
|
||
|
|
const variableRegex = /(local|static)?\s?(\w+)\s*(?:=|<-)/g;
|
||
|
|
const text = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
|
||
|
|
|
||
|
|
let match;
|
||
|
|
const variables = new Set<string>(); // 使用Set避免重复
|
||
|
|
|
||
|
|
while ((match = variableRegex.exec(text)) !== null) {
|
||
|
|
const variableName = match[2];
|
||
|
|
if (variableName && !variables.has(variableName)) {
|
||
|
|
variables.add(variableName);
|
||
|
|
const item = new vscode.CompletionItem(variableName, vscode.CompletionItemKind.Variable);
|
||
|
|
item.detail = '局部变量';
|
||
|
|
completions.push(item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return completions;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 点号触发的自动完成提供者类(用于对象属性和方法)
|
||
|
|
export class DotCompletionProvider implements vscode.CompletionItemProvider {
|
||
|
|
async provideCompletionItems(
|
||
|
|
document: vscode.TextDocument,
|
||
|
|
position: vscode.Position,
|
||
|
|
token: vscode.CancellationToken,
|
||
|
|
context: vscode.CompletionContext
|
||
|
|
): Promise<vscode.CompletionItem[]> {
|
||
|
|
// 查找最后一个点号的位置
|
||
|
|
const line = document.lineAt(position.line);
|
||
|
|
const dotIdx = line.text.lastIndexOf('.', position.character);
|
||
|
|
if (dotIdx === -1) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查是否在注释中,如果在注释中则不提供自动完成
|
||
|
|
const commentIndex = line.text.indexOf('//');
|
||
|
|
if (commentIndex >= 0 && position.character > commentIndex) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取点号前的单词(对象名)
|
||
|
|
const range = document.getWordRangeAtPosition(position.with(position.line, position.character - 1));
|
||
|
|
if (!range) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
const word = document.getText(range);
|
||
|
|
const completions: vscode.CompletionItem[] = [];
|
||
|
|
|
||
|
|
// 这里应该根据对象类型提供相应的属性和方法
|
||
|
|
// 由于这是一个简化实现,我们提供一些常见的对象方法
|
||
|
|
if (word === 'string' || word === 'String') {
|
||
|
|
completions.push(...this.getStringMethodCompletions());
|
||
|
|
} else if (word === 'array' || word === 'Array') {
|
||
|
|
completions.push(...this.getArrayMethodCompletions());
|
||
|
|
} else if (word === 'table' || word === 'Table') {
|
||
|
|
completions.push(...this.getTableMethodCompletions());
|
||
|
|
}
|
||
|
|
|
||
|
|
return completions;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取字符串方法完成项
|
||
|
|
private getStringMethodCompletions(): vscode.CompletionItem[] {
|
||
|
|
const methods = [
|
||
|
|
{ name: 'len', signature: 'function len()', description: '返回字符串长度' },
|
||
|
|
{ name: 'tointeger', signature: 'function tointeger()', description: '将字符串转换为整数' },
|
||
|
|
{ name: 'tofloat', signature: 'function tofloat()', description: '将字符串转换为浮点数' },
|
||
|
|
{ name: 'tostring', signature: 'function tostring()', description: '返回字符串本身' },
|
||
|
|
{ name: 'slice', signature: 'function slice(start, end)', description: '返回字符串的子串' },
|
||
|
|
{ name: 'find', signature: 'function find(substr)', description: '查找子串在字符串中的位置' },
|
||
|
|
{ name: 'tolower', signature: 'function tolower()', description: '转换为小写' },
|
||
|
|
{ name: 'toupper', signature: 'function toupper()', description: '转换为大写' }
|
||
|
|
];
|
||
|
|
|
||
|
|
return methods.map(method => {
|
||
|
|
const item = new vscode.CompletionItem(method.name, vscode.CompletionItemKind.Method);
|
||
|
|
item.detail = '字符串方法';
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${method.signature}\n\`\`\`\n${method.description}`);
|
||
|
|
item.insertText = method.name;
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取数组方法完成项
|
||
|
|
private getArrayMethodCompletions(): vscode.CompletionItem[] {
|
||
|
|
const methods = [
|
||
|
|
{ name: 'len', signature: 'function len()', description: '返回数组长度' },
|
||
|
|
{ name: 'append', signature: 'function append(value)', description: '向数组末尾添加元素' },
|
||
|
|
{ name: 'push', signature: 'function push(value)', description: '向数组末尾添加元素' },
|
||
|
|
{ name: 'pop', signature: 'function pop()', description: '移除并返回数组最后一个元素' },
|
||
|
|
{ name: 'resize', signature: 'function resize(newsize)', description: '调整数组大小' },
|
||
|
|
{ name: 'sort', signature: 'function sort()', description: '对数组进行排序' },
|
||
|
|
{ name: 'reverse', signature: 'function reverse()', description: '反转数组元素顺序' }
|
||
|
|
];
|
||
|
|
|
||
|
|
return methods.map(method => {
|
||
|
|
const item = new vscode.CompletionItem(method.name, vscode.CompletionItemKind.Method);
|
||
|
|
item.detail = '数组方法';
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${method.signature}\n\`\`\`\n${method.description}`);
|
||
|
|
item.insertText = method.name;
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取表方法完成项
|
||
|
|
private getTableMethodCompletions(): vscode.CompletionItem[] {
|
||
|
|
const methods = [
|
||
|
|
{ name: 'len', signature: 'function len()', description: '返回表中键值对的数量' },
|
||
|
|
{ name: 'rawget', signature: 'function rawget(key)', description: '获取指定键的值' },
|
||
|
|
{ name: 'rawset', signature: 'function rawset(key, value)', description: '设置指定键的值' },
|
||
|
|
{ name: 'rawdelete', signature: 'function rawdelete(key)', description: '删除指定键' },
|
||
|
|
{ name: 'setdelegate', signature: 'function setdelegate(delegate)', description: '设置委托表' },
|
||
|
|
{ name: 'getdelegate', signature: 'function getdelegate()', description: '获取委托表' }
|
||
|
|
];
|
||
|
|
|
||
|
|
return methods.map(method => {
|
||
|
|
const item = new vscode.CompletionItem(method.name, vscode.CompletionItemKind.Method);
|
||
|
|
item.detail = '表方法';
|
||
|
|
item.documentation = new vscode.MarkdownString(`\`\`\`squirrel\n${method.signature}\n\`\`\`\n${method.description}`);
|
||
|
|
item.insertText = method.name;
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|