1.0.0 初始版

This commit is contained in:
睿 安
2025-09-17 10:54:25 +08:00
commit dc0cbe71dc
315 changed files with 528712 additions and 0 deletions

211
src/apiClient.ts Normal file
View File

@@ -0,0 +1,211 @@
import * as vscode from 'vscode';
import * as http from 'http';
import * as https from 'https';
// API 响应接口
interface ApiResponse<T> {
Data: T;
IsError: boolean;
Msg: string | null;
}
// 获取文件列表响应
interface FileListResponse {
Data: string[];
IsError: boolean;
Msg: string | null;
}
// 获取文件内容响应
interface FileContentResponse {
Data: {
FileContentData: { [key: string]: string };
};
IsError: boolean;
Msg: string | null;
}
export class ApiClient {
private baseUrl: string;
private port: number;
constructor(baseUrl: string = 'http://localhost', port: number = 8080) {
this.baseUrl = baseUrl;
this.port = port;
}
private getFullUrl(endpoint: string): string {
return `${this.baseUrl}:${this.port}${endpoint}`;
}
// 通用的 HTTP 请求方法
private async request(url: string, method: string = 'GET', body?: string, headers: Record<string, string> = {}): Promise<any> {
return new Promise((resolve, reject) => {
const urlObj = new URL(url);
const isHttps = urlObj.protocol === 'https:';
const client = isHttps ? https : http;
const options: http.RequestOptions = {
hostname: urlObj.hostname,
port: urlObj.port || (isHttps ? 443 : 80),
path: urlObj.pathname + urlObj.search,
method: method,
headers: {
'User-Agent': 'VSCode-Squirrel-NUT-Explorer/1.0',
...headers
}
};
console.log(`HTTP请求: ${method} ${url}`);
console.log(`请求选项:`, options);
const req = client.request(options, (res) => {
let data = '';
console.log(`响应状态码: ${res.statusCode}`);
console.log(`响应头:`, res.headers);
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(`原始响应数据长度: ${data.length}`);
console.log(`原始响应数据: ${data}`);
if (res.statusCode !== 200) {
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
return;
}
try {
if (!data || data.trim() === '') {
reject(new Error('响应为空'));
return;
}
const result = JSON.parse(data);
resolve(result);
} catch (error) {
reject(new Error(`解析JSON响应失败: ${error}。原始数据: ${data}`));
}
});
});
req.on('error', (error) => {
console.error(`请求错误: ${error.message}`);
reject(new Error(`请求失败: ${error.message}`));
});
req.setTimeout(10000, () => {
console.error('请求超时');
req.destroy();
reject(new Error('请求超时'));
});
if (body) {
console.log(`请求体长度: ${body.length}`);
req.write(body);
}
req.end();
});
}
// 获取 nut 文件列表
async getFileList(dirName: string = 'sqr', fileType: string = 'nut'): Promise<string> {
try {
const url = this.getFullUrl('/Api/PvfUtiltiy/GetFileList');
const params = new URLSearchParams({
dirName,
returnType: '1',
fileType
});
const fullUrl = `${url}?${params}`;
console.log(`请求URL: ${fullUrl}`);
const result = await this.request(fullUrl) as { Data: string; IsError: boolean; Msg: string | null };
if (result.IsError) {
throw new Error(result.Msg || '获取文件列表失败');
}
return result.Data;
} catch (error) {
vscode.window.showErrorMessage(`获取文件列表失败: ${error}`);
throw error;
}
}
// 批量获取文件内容
async getFileContents(filePaths: string[], encodingType: string | null = null): Promise<{ [key: string]: string }> {
try {
const url = this.getFullUrl('/Api/PvfUtiltiy/GetFileContents');
const requestBody = {
FileList: filePaths,
UseCompatibleDecompiler: false,
EncodingType: encodingType
};
const result = await this.request(url, 'POST', JSON.stringify(requestBody), {
'Content-Type': 'application/json'
}) as FileContentResponse;
if (result.IsError) {
throw new Error(result.Msg || '获取文件内容失败');
}
return result.Data.FileContentData;
} catch (error) {
vscode.window.showErrorMessage(`获取文件内容失败: ${error}`);
throw error;
}
}
// 上传文件内容
async uploadFileContent(filePath: string, content: string): Promise<boolean> {
try {
const url = this.getFullUrl(`/Api/PvfUtiltiy/ImportFile?filePath=${encodeURIComponent(filePath)}`);
const result = await this.request(url, 'POST', content, {
'Content-Type': 'text/plain; charset=utf-8',
});
if (result.IsError) {
throw new Error(result.Msg || '上传文件失败');
}
return true;
} catch (error) {
vscode.window.showErrorMessage(`上传文件失败: ${error}`);
throw error;
}
}
// 获取 pvfUtility 版本号
async getVersion(): Promise<string> {
try {
const url = this.getFullUrl('/Api/PvfUtiltiy/getVersion');
console.log(`版本检查URL: ${url}`);
const result = await this.request(url) as { Data: string; IsError: boolean; Msg: string | null };
if (result.IsError) {
throw new Error(result.Msg || '获取版本号失败');
}
return result.Data;
} catch (error) {
console.error('获取版本号失败:', error);
vscode.window.showErrorMessage(`获取 pvfUtility 版本号失败: ${error}`);
throw error;
}
}
// 更新配置
updateConfig(baseUrl: string, port: number) {
this.baseUrl = baseUrl;
this.port = port;
}
}

102
src/commands.ts Normal file
View File

@@ -0,0 +1,102 @@
import * as vscode from 'vscode';
import { FileModel, FileEntry } from './model';
import { FileProvider } from './provider';
import { FunctionExtractor } from './functionExtractor';
export function registerCommands(context: vscode.ExtensionContext, model: FileModel, provider: FileProvider, functionExtractor: FunctionExtractor, output?: vscode.OutputChannel) {
// 连接到 API
console.log('注册 squirrel.connectToApi 命令...');
const connectCommand = vscode.commands.registerCommand('squirrel.connectToApi', async () => {
try {
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: '正在连接到 pvfUtility API...',
cancellable: false
}, async (progress) => {
const success = await model.connect();
if (success) {
vscode.window.showInformationMessage('成功连接到 pvfUtility API');
provider.refresh();
// 连接成功后,自动提取所有文件中的函数信息
console.log('开始提取所有文件中的函数信息...');
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: '正在提取函数信息...',
cancellable: false
}, async (progress) => {
await functionExtractor.extractAllFunctions(model);
vscode.window.showInformationMessage('函数信息提取完成');
});
} else {
vscode.window.showErrorMessage('连接到 pvfUtility API 失败');
}
});
} catch (error) {
vscode.window.showErrorMessage(`连接失败: ${error}`);
}
});
// 刷新文件列表
const refreshCommand = vscode.commands.registerCommand('squirrel.refreshFiles', async () => {
if (!model.getIsConnected()) {
vscode.window.showErrorMessage('请先连接到 pvfUtility API');
return;
}
try {
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: '正在刷新文件列表...',
cancellable: false
}, async (progress) => {
const success = await model.refresh();
if (success) {
vscode.window.showInformationMessage('文件列表刷新成功');
provider.refresh();
} else {
vscode.window.showErrorMessage('刷新文件列表失败');
}
});
} catch (error) {
vscode.window.showErrorMessage(`刷新失败: ${error}`);
}
});
// 打开文件
const openFileCommand = vscode.commands.registerCommand('squirrel.openFile', async (entry: FileEntry) => {
if (!model.getIsConnected()) {
vscode.window.showErrorMessage('请先连接到 pvfUtility API');
return;
}
try {
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: `正在加载文件 ${entry.name}...`,
cancellable: false
}, async (progress) => {
const content = await model.getFileContent(entry.key);
if (content !== undefined) {
// 创建虚拟文档
const uri = vscode.Uri.parse(`squirrel:/${entry.key}`);
const doc = await vscode.workspace.openTextDocument(uri);
await vscode.window.showTextDocument(doc);
provider.refresh();
} else {
vscode.window.showErrorMessage(`加载文件 ${entry.name} 失败`);
}
});
} catch (error) {
vscode.window.showErrorMessage(`打开文件失败: ${error}`);
}
});
// 保存文件(通过 VS Code 的保存事件处理,这个命令主要用于显示)
const saveFileCommand = vscode.commands.registerCommand('squirrel.saveFile', async () => {
vscode.window.showInformationMessage('使用 VS Code 的保存功能或 Ctrl+S 保存文件');
});
context.subscriptions.push(connectCommand, refreshCommand, openFileCommand, saveFileCommand);
}

161
src/extension.ts Normal file
View File

@@ -0,0 +1,161 @@
import * as vscode from 'vscode';
import { FileModel } from './model';
import { FileProvider } from './provider';
import { SquirrelFileSystemProvider } from './fileSystemProvider';
import { registerCommands } from './commands';
import { FunctionExtractor } from './functionExtractor';
import { CompletionProvider, DotCompletionProvider } from './providers/completionProvider';
import { HoverProvider } from './providers/hoverProvider';
import { DefinitionProvider } from './providers/definitionProvider';
import { SignatureHelpProvider } from './providers/signatureHelpProvider';
import { OnTypeFormattingProvider } from './providers/onTypeFormattingProvider';
import { DocumentFormattingProvider } from './providers/documentFormattingProvider';
import { CodeErrorProvider } from './providers/codeErrorProvider';
export function activate(context: vscode.ExtensionContext) {
console.log('Squirrel NUT Explorer 正在激活...');
// 创建模型和提供者
const model = new FileModel();
const provider = new FileProvider(model);
const functionExtractor = new FunctionExtractor();
const output = vscode.window.createOutputChannel('Squirrel');
// 创建自动完成提供者
const completionProvider = new CompletionProvider(functionExtractor.getCacheManager());
const dotCompletionProvider = new DotCompletionProvider();
// 创建悬停信息提供者
const hoverProvider = new HoverProvider(functionExtractor.getCacheManager());
// 创建定义跳转提供者
const definitionProvider = new DefinitionProvider(functionExtractor.getCacheManager());
// 创建签名帮助提供者
const signatureHelpProvider = new SignatureHelpProvider(functionExtractor.getCacheManager());
// 创建输入时格式化提供者
const onTypeFormattingProvider = new OnTypeFormattingProvider();
// 创建文档格式化提供者
const documentFormattingProvider = new DocumentFormattingProvider();
// 创建代码错误检测提供者
const codeErrorProvider = new CodeErrorProvider();
const diagnosticCollection = vscode.languages.createDiagnosticCollection('squirrel');
// 注册树形数据提供者
vscode.window.registerTreeDataProvider('squirrelExplorerView', provider);
// 注册自动完成提供者
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider('squirrel', completionProvider, ''),
vscode.languages.registerCompletionItemProvider('squirrel', dotCompletionProvider, '.')
);
// 注册悬停信息提供者
context.subscriptions.push(
vscode.languages.registerHoverProvider('squirrel', hoverProvider)
);
// 注册定义跳转提供者
context.subscriptions.push(
vscode.languages.registerDefinitionProvider('squirrel', definitionProvider)
);
// 注册签名帮助提供者
context.subscriptions.push(
vscode.languages.registerSignatureHelpProvider('squirrel', signatureHelpProvider, '(', ',')
);
// 注册输入时格式化提供者
context.subscriptions.push(
vscode.languages.registerOnTypeFormattingEditProvider('squirrel', onTypeFormattingProvider, ')', ';', '}')
);
// 注册文档格式化提供者
context.subscriptions.push(
vscode.languages.registerDocumentFormattingEditProvider('squirrel', documentFormattingProvider)
);
// 注册代码错误检测提供者
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider('squirrel', codeErrorProvider),
diagnosticCollection
);
// 注册文档诊断监听器
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument(event => {
if (event.document.languageId === 'squirrel') {
CodeErrorProvider.provideDiagnostics(event.document, diagnosticCollection);
}
}),
vscode.window.onDidChangeActiveTextEditor(editor => {
if (editor && editor.document.languageId === 'squirrel') {
CodeErrorProvider.provideDiagnostics(editor.document, diagnosticCollection);
}
}),
vscode.workspace.onDidOpenTextDocument(document => {
if (document.languageId === 'squirrel') {
CodeErrorProvider.provideDiagnostics(document, diagnosticCollection);
}
})
);
// 对所有已打开的squirrel文档进行初始诊断
vscode.workspace.textDocuments.forEach(document => {
if (document.languageId === 'squirrel') {
CodeErrorProvider.provideDiagnostics(document, diagnosticCollection);
}
});
// 注册文件系统提供者
const fileSystemProvider = new SquirrelFileSystemProvider(model);
vscode.workspace.registerFileSystemProvider('squirrel', fileSystemProvider, {
isCaseSensitive: true
});
// 注册所有命令
console.log('正在注册命令...');
registerCommands(context, model, provider, functionExtractor, output);
console.log('命令注册完成');
// 注册工作区配置变更监听
const configChangeListener = vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('squirrel')) {
model.updateConfig();
output.appendLine('[Squirrel] 配置已更新');
}
});
// 注册文件保存事件
const saveListener = vscode.workspace.onDidSaveTextDocument(async (doc) => {
if (doc.uri.scheme === 'squirrel') {
const filePath = doc.uri.path.substring(1); // 去掉开头的 '/'
const success = await model.saveFileContent(filePath, doc.getText());
if (success) {
output.appendLine(`[Squirrel] 文件 ${filePath} 保存成功`);
provider.refresh();
// 更新函数缓存
await functionExtractor.updateFileCache(model, filePath);
} else {
output.appendLine(`[Squirrel] 文件 ${filePath} 保存失败`);
}
}
});
context.subscriptions.push(configChangeListener, saveListener);
output.appendLine('[Squirrel] 扩展激活完成');
output.show();
// 显示欢迎信息
vscode.window.showInformationMessage('Squirrel NUT Explorer 已激活,请点击"连接到 pvfUtility API"开始使用');
}
export function deactivate() {
console.log('Squirrel NUT Explorer 正在停用...');
}

74
src/fileSystemProvider.ts Normal file
View File

@@ -0,0 +1,74 @@
import * as vscode from 'vscode';
import { FileModel } from './model';
export class SquirrelFileSystemProvider implements vscode.FileSystemProvider {
private model: FileModel;
private _onDidChangeFile = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
onDidChangeFile = this._onDidChangeFile.event;
constructor(model: FileModel) {
this.model = model;
}
// 读取文件
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
const filePath = this.getPathFromUri(uri);
const content = await this.model.getFileContent(filePath);
if (content === undefined) {
throw vscode.FileSystemError.FileNotFound(uri);
}
return new TextEncoder().encode(content);
}
// 写入文件
async writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean }): Promise<void> {
const filePath = this.getPathFromUri(uri);
const textContent = new TextDecoder().decode(content);
const success = await this.model.saveFileContent(filePath, textContent);
if (!success) {
throw vscode.FileSystemError.Unavailable('保存文件失败');
}
}
// 其他必须实现的方法(简化实现)
stat(uri: vscode.Uri): vscode.FileStat {
return {
type: vscode.FileType.File,
ctime: Date.now(),
mtime: Date.now(),
size: 0
};
}
readDirectory(uri: vscode.Uri): [string, vscode.FileType][] {
return [];
}
createDirectory(uri: vscode.Uri): void {
throw vscode.FileSystemError.NoPermissions('不支持创建目录');
}
delete(uri: vscode.Uri): void {
throw vscode.FileSystemError.NoPermissions('不支持删除文件');
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void {
throw vscode.FileSystemError.NoPermissions('不支持重命名文件');
}
watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[] }): vscode.Disposable {
return new vscode.Disposable(() => {});
}
// 从 URI 解析文件路径
private getPathFromUri(uri: vscode.Uri): string {
if (uri.scheme !== 'squirrel') {
throw vscode.FileSystemError.FileNotFound(uri);
}
return uri.path.substring(1); // 去掉开头的 '/'
}
}

315
src/functionExtractor.ts Normal file
View File

@@ -0,0 +1,315 @@
import * as vscode from 'vscode';
import { FileModel, FileEntry } from './model';
// 性能监控类
export class PerformanceMonitor {
private extractionTimes: number[] = [];
private fileCounts: number[] = [];
private functionCounts: number[] = [];
// 记录函数提取性能
recordExtraction(timeMs: number, fileCount: number, functionCount: number): void {
this.extractionTimes.push(timeMs);
this.fileCounts.push(fileCount);
this.functionCounts.push(functionCount);
}
// 获取平均提取时间
getAverageExtractionTime(): number {
if (this.extractionTimes.length === 0) return 0;
const sum = this.extractionTimes.reduce((a, b) => a + b, 0);
return sum / this.extractionTimes.length;
}
// 获取平均每文件处理时间
getAverageTimePerFile(): number {
if (this.fileCounts.length === 0 || this.extractionTimes.length === 0) return 0;
const totalTime = this.extractionTimes.reduce((a, b) => a + b, 0);
const totalFiles = this.fileCounts.reduce((a, b) => a + b, 0);
return totalFiles > 0 ? totalTime / totalFiles : 0;
}
// 获取性能统计信息
getStats(): {
averageExtractionTime: number;
averageTimePerFile: number;
totalExtractions: number;
} {
return {
averageExtractionTime: this.getAverageExtractionTime(),
averageTimePerFile: this.getAverageTimePerFile(),
totalExtractions: this.extractionTimes.length
};
}
// 清除统计数据
clearStats(): void {
this.extractionTimes = [];
this.fileCounts = [];
this.functionCounts = [];
}
}
// 函数信息接口
export interface FunctionInfo {
name: string; // 函数名称
filePath: string; // 文件路径
lineNumber: number; // 行号
parameters: string[]; // 参数列表
signature: string; // 函数签名
}
// 函数缓存管理类
export class FunctionCacheManager {
private functionCache = new Map<string, FunctionInfo[]>();
private fileProcessed = new Set<string>();
// 添加函数信息到缓存
addFunctions(filePath: string, functions: FunctionInfo[]): void {
this.functionCache.set(filePath, functions);
this.fileProcessed.add(filePath);
}
// 获取文件的函数信息
getFunctions(filePath: string): FunctionInfo[] | undefined {
return this.functionCache.get(filePath);
}
// 检查文件是否已处理
isFileProcessed(filePath: string): boolean {
return this.fileProcessed.has(filePath);
}
// 清除特定文件的缓存
clearFileCache(filePath: string): void {
this.functionCache.delete(filePath);
this.fileProcessed.delete(filePath);
}
// 清除所有缓存
clearAllCache(): void {
this.functionCache.clear();
this.fileProcessed.clear();
}
// 获取缓存统计信息
getCacheStats(): { fileCount: number; functionCount: number } {
let functionCount = 0;
for (const functions of this.functionCache.values()) {
functionCount += functions.length;
}
return {
fileCount: this.functionCache.size,
functionCount: functionCount
};
}
// 获取所有缓存的函数信息
getAllFunctions(): FunctionInfo[] {
const allFunctions: FunctionInfo[] = [];
for (const functions of this.functionCache.values()) {
allFunctions.push(...functions);
}
return allFunctions;
}
// 根据函数名称查找函数信息
findFunctionsByName(functionName: string): FunctionInfo[] {
const allFunctions = this.getAllFunctions();
return allFunctions.filter(func => func.name === functionName);
}
// 根据文件路径查找函数信息
findFunctionsByFilePath(filePath: string): FunctionInfo[] {
return this.getFunctions(filePath) || [];
}
}
// 函数提取器类
export class FunctionExtractor {
private cacheManager: FunctionCacheManager;
private isExtracting = false; // 防止重复提取
private performanceMonitor: PerformanceMonitor;
constructor() {
this.cacheManager = new FunctionCacheManager();
this.performanceMonitor = new PerformanceMonitor();
}
// 获取缓存管理器
getCacheManager(): FunctionCacheManager {
return this.cacheManager;
}
// 获取性能监控器
getPerformanceMonitor(): PerformanceMonitor {
return this.performanceMonitor;
}
// 检查是否正在提取函数
isExtractingFunctions(): boolean {
return this.isExtracting;
}
// 从文件内容中提取函数信息
extractFunctionsFromFile(filePath: string, content: string): FunctionInfo[] {
const functions: FunctionInfo[] = [];
const lines = content.split('\n');
// Squirrel语言函数定义的正则表达式
// 匹配类似: function functionName(param1, param2, ...)
const functionRegex = /^\s*function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(([^)]*)\)/;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const match = line.match(functionRegex);
if (match) {
const functionName = match[1];
const paramsString = match[2];
const parameters = this.parseParameters(paramsString);
const functionInfo: FunctionInfo = {
name: functionName,
filePath: filePath,
lineNumber: i + 1,
parameters: parameters,
signature: `function ${functionName}(${paramsString})`
};
functions.push(functionInfo);
}
}
return functions;
}
// 解析参数字符串
private parseParameters(paramsString: string): string[] {
if (!paramsString.trim()) {
return [];
}
// 简单参数解析,按逗号分割
return paramsString.split(',').map(param => param.trim()).filter(param => param.length > 0);
}
// 批量提取函数信息
async extractFunctionsFromFiles(model: FileModel, filePaths: string[]): Promise<void> {
// 防止重复提取
if (this.isExtracting) {
console.log('函数提取已在进行中,跳过本次请求');
return;
}
this.isExtracting = true;
console.log(`开始提取 ${filePaths.length} 个文件中的函数信息`);
try {
// 分批处理文件,避免一次性处理过多文件
const batchSize = 50;
for (let i = 0; i < filePaths.length; i += batchSize) {
const batch = filePaths.slice(i, i + batchSize);
console.log(`正在处理批次 ${Math.floor(i/batchSize) + 1}/${Math.ceil(filePaths.length/batchSize)}: ${batch.length} 个文件`);
try {
// 并行处理批次中的文件
const promises = batch.map(async (filePath) => {
// 检查文件是否已处理过
if (this.cacheManager.isFileProcessed(filePath)) {
return;
}
try {
const entry = model.getFileEntry(filePath);
if (!entry) {
console.warn(`文件 ${filePath} 不存在于文件列表中`);
return;
}
// 如果文件内容尚未加载,则加载内容
let content = entry.content;
if (!content) {
content = await model.getFileContent(filePath);
}
if (content) {
const functions = this.extractFunctionsFromFile(filePath, content);
this.cacheManager.addFunctions(filePath, functions);
console.log(`文件 ${filePath} 提取到 ${functions.length} 个函数`);
}
} catch (error) {
console.error(`处理文件 ${filePath} 时出错:`, error);
}
});
// 等待批次处理完成
await Promise.all(promises);
console.log(`批次 ${Math.floor(i/batchSize) + 1} 处理完成`);
} catch (batchError) {
console.error(`处理批次 ${Math.floor(i/batchSize) + 1} 时出错:`, batchError);
}
}
console.log('所有文件的函数信息提取完成');
} finally {
this.isExtracting = false;
}
}
// 提取所有文件的函数信息
async extractAllFunctions(model: FileModel): Promise<void> {
const startTime = Date.now();
// 获取所有nut文件路径
const filePaths = model.getAllNutFiles();
console.log(`准备提取 ${filePaths.length} 个nut文件的函数信息`);
if (filePaths.length === 0) {
console.log('没有nut文件需要处理');
return;
}
await this.extractFunctionsFromFiles(model, filePaths);
// 输出缓存统计信息
const stats = this.cacheManager.getCacheStats();
const endTime = Date.now();
const durationMs = endTime - startTime;
const durationSec = durationMs / 1000; // 转换为秒
// 记录性能数据
this.performanceMonitor.recordExtraction(durationMs, filePaths.length, stats.functionCount);
console.log(`函数缓存统计: ${stats.fileCount} 个文件, ${stats.functionCount} 个函数`);
console.log(`函数提取完成,耗时: ${durationSec.toFixed(2)}`);
// 输出性能统计信息
const perfStats = this.performanceMonitor.getStats();
console.log(`性能统计: 平均提取时间 ${perfStats.averageExtractionTime.toFixed(2)}ms, 平均每文件处理时间 ${perfStats.averageTimePerFile.toFixed(2)}ms`);
}
// 更新特定文件的函数缓存
async updateFileCache(model: FileModel, filePath: string): Promise<void> {
console.log(`更新文件 ${filePath} 的函数缓存`);
try {
// 获取文件内容
const content = await model.getFileContent(filePath);
if (content) {
// 提取函数信息
const functions = this.extractFunctionsFromFile(filePath, content);
// 更新缓存
this.cacheManager.addFunctions(filePath, functions);
console.log(`文件 ${filePath} 的函数缓存已更新,提取到 ${functions.length} 个函数`);
} else {
console.warn(`无法获取文件 ${filePath} 的内容`);
}
} catch (error) {
console.error(`更新文件 ${filePath} 的函数缓存时出错:`, error);
}
}
}

293
src/model.ts Normal file
View File

@@ -0,0 +1,293 @@
import * as vscode from 'vscode';
import { ApiClient } from './apiClient';
export interface FileEntry {
key: string; // 文件路径
name: string; // 显示名称
isFile: boolean;
content?: string; // 文件内容
lastLoaded?: Date; // 最后加载时间
}
export class FileModel {
private fileList = new Map<string, FileEntry>();
private apiClient: ApiClient;
private config: vscode.WorkspaceConfiguration;
private isConnected = false;
constructor() {
this.apiClient = new ApiClient();
this.config = vscode.workspace.getConfiguration('squirrel');
}
// 更新配置
updateConfig() {
this.config = vscode.workspace.getConfiguration('squirrel');
const baseUrl = this.config.get<string>('api.baseUrl', 'http://localhost');
const port = this.config.get<number>('api.port', 8080);
this.apiClient.updateConfig(baseUrl, port);
}
// 连接到API
async connect(): Promise<boolean> {
try {
this.updateConfig();
const baseUrl = this.config.get<string>('api.baseUrl', 'http://localhost');
const port = this.config.get<number>('api.port', 8080);
const defaultDir = this.config.get<string>('defaultDirectory', 'sqr');
console.log(`尝试连接到: ${baseUrl}:${port}, 目录: ${defaultDir}`);
// 首先检查版本号
console.log('正在检查 pvfUtility 版本号...');
const version = await this.apiClient.getVersion();
console.log(`pvfUtility 版本: ${version}`);
// 测试连接并获取文件列表
const filesData = await this.apiClient.getFileList(defaultDir);
console.log(`API返回数据: ${JSON.stringify(filesData)}`);
// 将字符串按换行符分割成数组
const files = filesData.split(/\r?\n/).filter((file: string) => file.trim() !== '');
console.log(`成功获取到 ${files.length} 个文件`);
this.fileList.clear();
files.forEach((filePath: string) => {
const entry: FileEntry = {
key: filePath,
name: filePath.split('/').pop() || filePath,
isFile: true
};
this.fileList.set(filePath, entry);
});
// 自动加载所有文件内容
console.log('开始自动加载所有文件内容...');
await this.loadAllFileContents();
this.isConnected = true;
return true;
} catch (error) {
console.error('连接失败详细信息:', error);
this.isConnected = false;
return false;
}
}
// 是否已连接
getIsConnected(): boolean {
return this.isConnected;
}
// 获取指定目录的子文件(支持树形结构)
getChildren(parentKey?: string): FileEntry[] {
if (!parentKey) {
// 获取根目录内容
const folders = new Map<string, string>(); // folderKey -> name
const files: FileEntry[] = [];
for (const key of this.fileList.keys()) {
const idx = key.indexOf('/');
if (idx === -1) {
// 根目录文件
const entry = this.fileList.get(key);
if (entry) files.push(entry);
} else {
// 根目录文件夹
const folder = key.substring(0, idx);
if (!folders.has(folder)) folders.set(folder, folder);
}
}
const dirs: FileEntry[] = [...folders.keys()].map(k => ({
key: k,
name: k,
isFile: false
}));
return [...files, ...dirs].sort((a, b) =>
(a.isFile === b.isFile) ?
a.name.localeCompare(b.name, 'en', { sensitivity: 'base' }) :
(a.isFile ? 1 : -1)
);
}
// 获取子目录内容
const prefix = parentKey.endsWith('/') ? parentKey : parentKey + '/';
const seenFolders = new Set<string>();
const result: FileEntry[] = [];
for (const key of this.fileList.keys()) {
if (!key.startsWith(prefix)) continue;
const rest = key.substring(prefix.length);
const slash = rest.indexOf('/');
if (slash === -1) {
// 直接子文件
const entry = this.fileList.get(key);
if (entry) result.push(entry);
} else {
// 子文件夹
const childFolder = rest.substring(0, slash);
const childKey = prefix + childFolder;
if (!seenFolders.has(childKey)) {
seenFolders.add(childKey);
result.push({
key: childKey,
name: childFolder,
isFile: false
});
}
}
}
result.sort((a, b) =>
(a.isFile === b.isFile) ?
a.name.localeCompare(b.name, 'en', { sensitivity: 'base' }) :
(a.isFile ? 1 : -1)
);
return result;
}
// 获取文件内容
async getFileContent(filePath: string): Promise<string | undefined> {
const entry = this.fileList.get(filePath);
if (!entry) {
return undefined;
}
// 如果已经加载过内容,直接返回
if (entry.content) {
return entry.content;
}
try {
// 获取编码设置
const encodingType = this.config.get<string>('nutEncoding', 'UTF8');
const contents = await this.apiClient.getFileContents([filePath], encodingType);
const content = contents[filePath];
if (content !== undefined) {
entry.content = content;
entry.lastLoaded = new Date();
return content;
}
} catch (error) {
vscode.window.showErrorMessage(`获取文件内容失败: ${error}`);
}
return undefined;
}
// 保存文件内容
async saveFileContent(filePath: string, content: string): Promise<boolean> {
try {
const success = await this.apiClient.uploadFileContent(filePath, content);
if (success) {
const entry = this.fileList.get(filePath);
if (entry) {
entry.content = content;
entry.lastLoaded = new Date();
}
return true;
}
} catch (error) {
vscode.window.showErrorMessage(`保存文件失败: ${error}`);
}
return false;
}
// 刷新文件列表
async refresh(): Promise<boolean> {
if (!this.isConnected) {
return false;
}
try {
this.updateConfig();
const defaultDir = this.config.get<string>('defaultDirectory', 'sqr');
const filesData = await this.apiClient.getFileList(defaultDir);
// 将字符串按换行符分割成数组
const files = filesData.split(/\r?\n/).filter((file: string) => file.trim() !== '');
this.fileList.clear();
files.forEach((filePath: string) => {
const entry: FileEntry = {
key: filePath,
name: filePath.split('/').pop() || filePath,
isFile: true
};
this.fileList.set(filePath, entry);
});
return true;
} catch (error) {
vscode.window.showErrorMessage(`刷新文件列表失败: ${error}`);
return false;
}
}
// 批量加载所有文件内容
async loadAllFileContents(): Promise<void> {
try {
// 获取所有文件路径
const filePaths = Array.from(this.fileList.keys());
if (filePaths.length === 0) {
console.log('没有文件需要加载');
return;
}
console.log(`准备加载 ${filePaths.length} 个文件的内容`);
// 获取编码设置
const encodingType = this.config.get<string>('nutEncoding', 'UTF8');
// 分批获取文件内容,避免一次性请求过多文件
const batchSize = 50; // 每批处理50个文件
for (let i = 0; i < filePaths.length; i += batchSize) {
const batch = filePaths.slice(i, i + batchSize);
console.log(`正在加载批次 ${Math.floor(i/batchSize) + 1}/${Math.ceil(filePaths.length/batchSize)}: ${batch.length} 个文件`);
try {
const contents = await this.apiClient.getFileContents(batch, encodingType);
// 更新文件内容
for (const [filePath, content] of Object.entries(contents)) {
const entry = this.fileList.get(filePath);
if (entry) {
entry.content = content;
entry.lastLoaded = new Date();
}
}
console.log(`批次 ${Math.floor(i/batchSize) + 1} 加载完成`);
} catch (batchError) {
console.error(`加载批次 ${Math.floor(i/batchSize) + 1} 失败:`, batchError);
// 继续处理下一个批次,不中断整个过程
}
}
console.log('所有文件内容加载完成');
} catch (error) {
console.error('批量加载文件内容失败:', error);
vscode.window.showErrorMessage(`批量加载文件内容失败: ${error}`);
}
}
// 获取文件条目
getFileEntry(filePath: string): FileEntry | undefined {
return this.fileList.get(filePath);
}
// 获取所有nut文件路径
getAllNutFiles(): string[] {
return Array.from(this.fileList.keys())
.filter(filePath => filePath.endsWith('.nut'));
}
}

74
src/provider.ts Normal file
View File

@@ -0,0 +1,74 @@
import * as vscode from 'vscode';
import { FileEntry, FileModel } from './model';
export class FileProvider implements vscode.TreeDataProvider<FileEntry> {
private _onDidChangeTreeData = new vscode.EventEmitter<FileEntry | undefined | void>();
onDidChangeTreeData = this._onDidChangeTreeData.event;
constructor(private model: FileModel, private output?: vscode.OutputChannel) {}
refresh(): void {
this._onDidChangeTreeData.fire();
}
getTreeItem(element: FileEntry): vscode.TreeItem {
const item = new vscode.TreeItem(
element.name,
element.isFile ? vscode.TreeItemCollapsibleState.None : vscode.TreeItemCollapsibleState.Collapsed
);
// 设置上下文值,用于右键菜单
item.contextValue = element.isFile ? 'squirrel.file' : 'squirrel.folder';
// 设置虚拟文件 URI
const uri = vscode.Uri.parse(`squirrel:/${element.key}`);
item.resourceUri = uri;
if (element.isFile) {
// 设置文件图标
item.iconPath = new vscode.ThemeIcon('file-code');
// 为文件设置点击命令
item.command = {
command: 'squirrel.openFile',
title: '打开',
arguments: [element]
};
// 如果有内容,显示状态
if (element.content) {
item.description = '已加载';
} else {
item.description = '未加载';
}
} else {
// 文件夹图标
item.iconPath = new vscode.ThemeIcon('folder');
}
return item;
}
getChildren(element?: FileEntry): Thenable<FileEntry[]> {
try {
const result = this.model.getChildren(element?.key);
if (this.output) {
const label = element ? `children:${element.key}` : 'children:<root>';
this.output.appendLine(`[Squirrel] get${label} -> ${result.length} items`);
}
return Promise.resolve(result);
} catch (error) {
if (this.output) {
this.output.appendLine(`[Squirrel] Error getting children: ${error}`);
}
return Promise.resolve([]);
}
}
getParent?(element: FileEntry): Thenable<FileEntry | undefined> {
// 简单实现,返回 undefined 表示根目录
return Promise.resolve(undefined);
}
}

452
src/providers/apiParser.ts Normal file
View File

@@ -0,0 +1,452 @@
import * as vscode from 'vscode';
// API函数信息接口
export interface ApiFunction {
name: string;
description: string;
params: ApiParameter[];
returns?: ApiReturn;
example?: string;
}
// API参数信息接口
export interface ApiParameter {
name: string;
type: string;
description: string;
optional?: boolean;
defaultValue?: string;
}
// API返回值信息接口
export interface ApiReturn {
type: string;
description: string;
}
// API类信息接口
export interface ApiClass {
name: string;
description: string;
methods: ApiFunction[];
properties: ApiProperty[];
}
// API属性信息接口
export interface ApiProperty {
name: string;
type: string;
description: string;
}
// API常量信息接口
export interface ApiConstant {
name: string;
value: string;
description: string;
category: string;
}
// API文档解析器类
export class ApiParser {
private static instance: ApiParser;
private functions: ApiFunction[] = [];
private classes: ApiClass[] = [];
private constants: ApiConstant[] = [];
private constructor() {
this.initializeApiDocumentation();
}
// 获取单例实例
public static getInstance(): ApiParser {
if (!ApiParser.instance) {
ApiParser.instance = new ApiParser();
}
return ApiParser.instance;
}
// 初始化API文档
private initializeApiDocumentation(): void {
// 初始化内置函数
this.functions = [
{
name: 'print',
description: '打印消息到控制台',
params: [
{
name: 'message',
type: 'string',
description: '要打印的消息'
}
],
returns: {
type: 'void',
description: '无返回值'
}
},
{
name: 'len',
description: '返回字符串、数组或表的长度',
params: [
{
name: 'obj',
type: 'string|table|array',
description: '要计算长度的对象'
}
],
returns: {
type: 'integer',
description: '对象的长度'
}
},
{
name: 'type',
description: '返回对象的类型',
params: [
{
name: 'obj',
type: 'any',
description: '要检查类型的对象'
}
],
returns: {
type: 'string',
description: '对象的类型字符串'
}
},
{
name: 'clone',
description: '创建对象的浅拷贝',
params: [
{
name: 'obj',
type: 'any',
description: '要克隆的对象'
}
],
returns: {
type: 'any',
description: '克隆的对象'
}
},
{
name: 'tostring',
description: '将对象转换为字符串',
params: [
{
name: 'obj',
type: 'any',
description: '要转换的对象'
}
],
returns: {
type: 'string',
description: '转换后的字符串'
}
},
{
name: 'tointeger',
description: '将对象转换为整数',
params: [
{
name: 'obj',
type: 'any',
description: '要转换的对象'
}
],
returns: {
type: 'integer',
description: '转换后的整数'
}
},
{
name: 'tofloat',
description: '将对象转换为浮点数',
params: [
{
name: 'obj',
type: 'any',
description: '要转换的对象'
}
],
returns: {
type: 'float',
description: '转换后的浮点数'
}
}
];
// 初始化内置类
this.classes = [
{
name: 'String',
description: '字符串类,提供字符串操作方法',
methods: [
{
name: 'len',
description: '返回字符串长度',
params: [],
returns: {
type: 'integer',
description: '字符串的长度'
}
},
{
name: 'slice',
description: '返回字符串的子串',
params: [
{
name: 'start',
type: 'integer',
description: '起始位置'
},
{
name: 'end',
type: 'integer',
description: '结束位置(可选)',
optional: true
}
],
returns: {
type: 'string',
description: '子串'
}
},
{
name: 'find',
description: '查找子串在字符串中的位置',
params: [
{
name: 'substr',
type: 'string',
description: '要查找的子串'
}
],
returns: {
type: 'integer',
description: '子串的位置,未找到返回-1'
}
}
],
properties: [
{
name: 'length',
type: 'integer',
description: '字符串的长度'
}
]
},
{
name: 'Array',
description: '数组类,提供数组操作方法',
methods: [
{
name: 'len',
description: '返回数组长度',
params: [],
returns: {
type: 'integer',
description: '数组的长度'
}
},
{
name: 'append',
description: '向数组末尾添加元素',
params: [
{
name: 'value',
type: 'any',
description: '要添加的元素'
}
],
returns: {
type: 'void',
description: '无返回值'
}
},
{
name: 'pop',
description: '移除并返回数组最后一个元素',
params: [],
returns: {
type: 'any',
description: '被移除的元素'
}
}
],
properties: [
{
name: 'length',
type: 'integer',
description: '数组的长度'
}
]
},
{
name: 'Table',
description: '表类,提供表操作方法',
methods: [
{
name: 'len',
description: '返回表中键值对的数量',
params: [],
returns: {
type: 'integer',
description: '键值对的数量'
}
},
{
name: 'rawget',
description: '获取指定键的值',
params: [
{
name: 'key',
type: 'any',
description: '键'
}
],
returns: {
type: 'any',
description: '键对应的值'
}
},
{
name: 'rawset',
description: '设置指定键的值',
params: [
{
name: 'key',
type: 'any',
description: '键'
},
{
name: 'value',
type: 'any',
description: '值'
}
],
returns: {
type: 'void',
description: '无返回值'
}
}
],
properties: []
}
];
// 初始化常量
this.constants = [
{
name: 'PI',
value: '3.14159',
description: '圆周率',
category: 'math'
},
{
name: 'E',
value: '2.71828',
description: '自然常数',
category: 'math'
},
{
name: 'true',
value: 'true',
description: '布尔真值',
category: 'boolean'
},
{
name: 'false',
value: 'false',
description: '布尔假值',
category: 'boolean'
},
{
name: 'null',
value: 'null',
description: '空值',
category: 'general'
}
];
}
// 获取所有函数
public getFunctions(): ApiFunction[] {
return this.functions;
}
// 根据名称获取函数
public getFunctionByName(name: string): ApiFunction | undefined {
return this.functions.find(func => func.name === name);
}
// 获取所有类
public getClasses(): ApiClass[] {
return this.classes;
}
// 根据名称获取类
public getClassByName(name: string): ApiClass | undefined {
return this.classes.find(cls => cls.name === name);
}
// 获取所有常量
public getConstants(): ApiConstant[] {
return this.constants;
}
// 根据类别获取常量
public getConstantsByCategory(category: string): ApiConstant[] {
return this.constants.filter(constant => constant.category === category);
}
// 根据名称获取常量
public getConstantByName(name: string): ApiConstant | undefined {
return this.constants.find(constant => constant.name === name);
}
// 生成函数签名
public generateFunctionSignature(func: ApiFunction): string {
const params = func.params.map(param => {
let paramStr = param.name;
if (param.optional) {
paramStr = `[${paramStr}`;
if (param.defaultValue) {
paramStr += `=${param.defaultValue}`;
}
paramStr += ']';
}
return paramStr;
}).join(', ');
return `function ${func.name}(${params})${func.returns ? `: ${func.returns.type}` : ': void'}`;
}
// 生成类签名
public generateClassSignature(cls: ApiClass): string {
return `class ${cls.name}`;
}
// 生成属性签名
public generatePropertySignature(prop: ApiProperty): string {
return `property ${prop.name}: ${prop.type}`;
}
// 生成方法签名
public generateMethodSignature(method: ApiFunction): string {
const params = method.params.map(param => {
let paramStr = param.name;
if (param.optional) {
paramStr = `[${paramStr}`;
if (param.defaultValue) {
paramStr += `=${param.defaultValue}`;
}
paramStr += ']';
}
return paramStr;
}).join(', ');
return `function ${method.name}(${params})${method.returns ? `: ${method.returns.type}` : ': void'}`;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,307 @@
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;
});
}
}

View File

@@ -0,0 +1,57 @@
import * as vscode from 'vscode';
import { FunctionCacheManager, FunctionInfo } from '../functionExtractor';
// 定义跳转提供者类
export class DefinitionProvider implements vscode.DefinitionProvider {
private cacheManager: FunctionCacheManager;
constructor(cacheManager: FunctionCacheManager) {
this.cacheManager = cacheManager;
}
// 提供符号定义位置
async provideDefinition(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken
): Promise<vscode.Definition | vscode.LocationLink[] | undefined> {
// 检查是否在注释中,如果在注释中则不提供定义跳转
const line = document.lineAt(position.line);
const commentIndex = line.text.indexOf('//');
if (commentIndex >= 0 && position.character > commentIndex) {
return undefined;
}
// 获取光标位置的单词范围
const range = document.getWordRangeAtPosition(position);
if (!range) {
return undefined;
}
// 获取单词
const word = document.getText(range);
// 查找函数定义
const functions = this.cacheManager.findFunctionsByName(word);
if (functions.length > 0) {
if (functions.length === 1) {
// 单个函数定义
const func = functions[0];
const uri = vscode.Uri.parse(`squirrel:/${func.filePath}`);
const position = new vscode.Position(func.lineNumber - 1, 0);
return new vscode.Location(uri, position);
} else {
// 多个同名函数定义,返回所有位置
const locations: vscode.Location[] = [];
functions.forEach(func => {
const uri = vscode.Uri.parse(`squirrel:/${func.filePath}`);
const position = new vscode.Position(func.lineNumber - 1, 0);
locations.push(new vscode.Location(uri, position));
});
return locations;
}
}
return undefined;
}
}

View File

@@ -0,0 +1,100 @@
import * as vscode from 'vscode';
// 文档格式化提供者类
export class DocumentFormattingProvider implements vscode.DocumentFormattingEditProvider {
// 提供整个文档的代码格式化功能
async provideDocumentFormattingEdits(
document: vscode.TextDocument,
options: vscode.FormattingOptions,
token: vscode.CancellationToken
): Promise<vscode.TextEdit[]> {
const edits: vscode.TextEdit[] = [];
const lines: string[] = [];
// 逐行处理文档内容
for (let i = 0; i < document.lineCount; i++) {
const line = document.lineAt(i);
lines.push(line.text);
}
// 格式化文档
const formattedLines = this.formatDocument(lines, options);
// 创建编辑操作
const fullRange = new vscode.Range(
document.positionAt(0),
document.positionAt(document.getText().length)
);
edits.push(vscode.TextEdit.replace(fullRange, formattedLines.join('\n')));
return edits;
}
// 格式化文档
private formatDocument(lines: string[], options: vscode.FormattingOptions): string[] {
const formattedLines: string[] = [];
let indentLevel = 0;
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
const trimmedLine = line.trim();
if (trimmedLine.length === 0) {
// 空行保持原样或删除
formattedLines.push('');
continue;
}
// 检查是否需要减少缩进(遇到右大括号等)
if (trimmedLine.startsWith('}') || trimmedLine.startsWith(')') || trimmedLine.startsWith(']')) {
indentLevel = Math.max(0, indentLevel - 1);
}
// 添加适当的缩进
const indent = this.createIndent(indentLevel, options);
const formattedLine = indent + trimmedLine;
formattedLines.push(formattedLine);
// 检查是否需要增加缩进(遇到左大括号等)
if (trimmedLine.endsWith('{') || trimmedLine.endsWith('(') || trimmedLine.endsWith('[')) {
indentLevel++;
}
// 特殊处理else、catch等关键字它们应该与前面的右大括号在同一行
if (trimmedLine.startsWith('else') || trimmedLine.startsWith('catch') || trimmedLine.startsWith('elif')) {
if (formattedLines.length > 1) {
const prevLine = formattedLines[formattedLines.length - 2];
if (prevLine.trim().endsWith('}')) {
// 将else等关键字与前面的右大括号放在同一行
formattedLines[formattedLines.length - 2] = prevLine.trim() + ' ' + trimmedLine;
formattedLines.pop(); // 移除当前行
indentLevel--; // 修正缩进级别
}
}
}
}
// 规范化空格(在操作符周围添加适当空格)
return formattedLines.map(line => this.normalizeSpaces(line));
}
// 在操作符周围添加适当空格
private normalizeSpaces(line: string): string {
// 在常见的操作符周围添加空格
return line
.replace(/([^\s])(==|!=|<=|>=|<|>|=|\+|-|\*|\/|%)([^\s])/g, '$1 $2 $3')
.replace(/([^\s])(,)([^\s])/g, '$1$2 $3')
.replace(/\s+/g, ' ') // 将多个空格替换为单个空格
.trim();
}
// 创建指定级别的缩进
private createIndent(level: number, options: vscode.FormattingOptions): string {
if (options.insertSpaces) {
return ' '.repeat(level * options.tabSize);
} else {
return '\t'.repeat(level);
}
}
}

View File

@@ -0,0 +1,179 @@
import * as vscode from 'vscode';
import { FunctionCacheManager, FunctionInfo } from '../functionExtractor';
import { ApiParser, ApiFunction, ApiClass, ApiConstant } from './apiParser';
// 悬停信息提供者类
export class HoverProvider implements vscode.HoverProvider {
private cacheManager: FunctionCacheManager;
private apiParser: ApiParser;
constructor(cacheManager: FunctionCacheManager) {
this.cacheManager = cacheManager;
this.apiParser = ApiParser.getInstance();
}
// 提供悬停时显示的信息
async provideHover(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken
): Promise<vscode.Hover | undefined> {
// 检查是否在注释中,如果在注释中则不提供悬停信息
const line = document.lineAt(position.line);
const commentIndex = line.text.indexOf('//');
if (commentIndex >= 0 && position.character > commentIndex) {
return undefined;
}
// 获取鼠标悬停位置的单词范围
const range = document.getWordRangeAtPosition(position);
if (!range) {
return undefined;
}
// 获取单词
const word = document.getText(range);
// 首先检查是否是API函数
const apiFunction = this.apiParser.getFunctionByName(word);
if (apiFunction) {
const signature = this.apiParser.generateFunctionSignature(apiFunction);
let hoverContent = `**内置函数**: \`${signature}\`\n\n`;
hoverContent += `${apiFunction.description}\n\n`;
if (apiFunction.params.length > 0) {
hoverContent += `**参数**:\n`;
apiFunction.params.forEach(param => {
hoverContent += `- \`${param.name}: ${param.type}\`${param.optional ? ' (可选)' : ''} - ${param.description}\n`;
});
}
if (apiFunction.returns) {
hoverContent += `\n**返回值**: \`${apiFunction.returns.type}\` - ${apiFunction.returns.description}`;
}
return new vscode.Hover(new vscode.MarkdownString(hoverContent), range);
}
// 检查是否是API类
const apiClass = this.apiParser.getClassByName(word);
if (apiClass) {
const signature = this.apiParser.generateClassSignature(apiClass);
let hoverContent = `**内置类**: \`${signature}\`\n\n`;
hoverContent += `${apiClass.description}\n\n`;
if (apiClass.methods.length > 0) {
hoverContent += `**方法**:\n`;
apiClass.methods.forEach(method => {
const methodSignature = this.apiParser.generateMethodSignature(method);
hoverContent += `- \`${methodSignature}\` - ${method.description}\n`;
});
}
if (apiClass.properties.length > 0) {
hoverContent += `\n**属性**:\n`;
apiClass.properties.forEach(prop => {
const propSignature = this.apiParser.generatePropertySignature(prop);
hoverContent += `- \`${propSignature}\` - ${prop.description}\n`;
});
}
return new vscode.Hover(new vscode.MarkdownString(hoverContent), range);
}
// 检查是否是API常量
const apiConstant = this.apiParser.getConstantByName(word);
if (apiConstant) {
let hoverContent = `**常量**: \`${apiConstant.name}\`\n\n`;
hoverContent += `值: ${apiConstant.value}\n\n`;
hoverContent += `${apiConstant.description}\n\n`;
hoverContent += `类别: ${apiConstant.category}`;
return new vscode.Hover(new vscode.MarkdownString(hoverContent), range);
}
// 查找函数信息
const functions = this.cacheManager.findFunctionsByName(word);
if (functions.length > 0) {
// 创建悬停信息
let hoverContent = '';
if (functions.length === 1) {
// 单个函数
const func = functions[0];
hoverContent = `**函数**: \`${func.signature}\`\n\n`;
hoverContent += `**文件**: ${func.filePath}\n\n`;
hoverContent += `**行号**: ${func.lineNumber}`;
} else {
// 多个同名函数
hoverContent = `**函数**: ${word}\n\n`;
hoverContent += `在多个文件中定义:\n\n`;
functions.forEach(func => {
hoverContent += `- \`${func.signature}\` (${func.filePath}:${func.lineNumber})\n`;
});
}
return new vscode.Hover(new vscode.MarkdownString(hoverContent), range);
}
// 检查是否是关键字
const keywordInfo = this.getKeywordInfo(word);
if (keywordInfo) {
return new vscode.Hover(new vscode.MarkdownString(keywordInfo), range);
}
// 检查是否是常量
const constantInfo = this.getConstantInfo(word);
if (constantInfo) {
return new vscode.Hover(new vscode.MarkdownString(constantInfo), range);
}
return undefined;
}
// 获取关键字信息
private getKeywordInfo(word: string): string | undefined {
const keywordMap: { [key: string]: string } = {
'if': '**条件语句**\n\n用于条件执行代码块',
'else': '**条件语句**\n\n与if语句配合使用当if条件不满足时执行',
'while': '**循环语句**\n\n当条件为真时重复执行代码块',
'for': '**循环语句**\n\n用于循环执行代码块',
'foreach': '**循环语句**\n\n遍历数组或表中的每个元素',
'function': '**函数定义**\n\n用于定义函数',
'local': '**变量声明**\n\n声明局部变量',
'return': '**返回语句**\n\n从函数中返回值',
'class': '**类定义**\n\n用于定义类',
'extends': '**继承**\n\n用于指定类的父类',
'constructor': '**构造函数**\n\n类的构造函数',
'null': '**空值**\n\n表示空值',
'true': '**布尔值**\n\n表示真值',
'false': '**布尔值**\n\n表示假值'
};
return keywordMap[word];
}
// 获取常量信息
private getConstantInfo(word: string): string | undefined {
const constantMap: { [key: string]: string } = {
'PI': '**数学常量**\n\n圆周率 π ≈ 3.14159',
'E': '**数学常量**\n\n自然常数 e ≈ 2.71828',
'RAND_MAX': '**随机数**\n\n随机数生成器的最大值',
'CHAR_BIT': '**字符**\n\n一个字节的位数',
'CHAR_MAX': '**字符**\n\nchar类型的最大值',
'CHAR_MIN': '**字符**\n\nchar类型的最小值',
'SHRT_MAX': '**短整型**\n\nshort类型的最大值',
'SHRT_MIN': '**短整型**\n\nshort类型的最小值',
'INT_MAX': '**整型**\n\nint类型的最大值',
'INT_MIN': '**整型**\n\nint类型的最小值',
'LONG_MAX': '**长整型**\n\nlong类型的最大值',
'LONG_MIN': '**长整型**\n\nlong类型的最小值',
'FLT_MAX': '**浮点型**\n\nfloat类型的最大值',
'FLT_MIN': '**浮点型**\n\nfloat类型的最小值',
'DBL_MAX': '**双精度浮点型**\n\ndouble类型的最大值',
'DBL_MIN': '**双精度浮点型**\n\ndouble类型的最小值'
};
return constantMap[word];
}
}

View File

@@ -0,0 +1,149 @@
import * as vscode from 'vscode';
// 输入时格式化提供者类
export class OnTypeFormattingProvider implements vscode.OnTypeFormattingEditProvider {
// 在用户输入特定字符时自动格式化代码
async provideOnTypeFormattingEdits(
document: vscode.TextDocument,
position: vscode.Position,
ch: string,
options: vscode.FormattingOptions,
token: vscode.CancellationToken
): Promise<vscode.TextEdit[]> {
const edits: vscode.TextEdit[] = [];
// 根据输入的字符执行不同的格式化操作
switch (ch) {
case ')':
// 输入右括号时格式化当前行
edits.push(...this.formatCurrentLine(document, position, options));
break;
case ';':
// 输入分号时格式化当前行
edits.push(...this.formatCurrentLine(document, position, options));
break;
case '}':
// 输入右大括号时格式化整个代码块
edits.push(...this.formatCodeBlock(document, position, options));
break;
default:
break;
}
return edits;
}
// 格式化当前行
private formatCurrentLine(
document: vscode.TextDocument,
position: vscode.Position,
options: vscode.FormattingOptions
): vscode.TextEdit[] {
const line = document.lineAt(position.line);
const trimmedLine = line.text.trim();
if (trimmedLine.length === 0) {
return [];
}
// 简单的格式化:确保行尾没有多余空格
if (line.text !== trimmedLine && !line.text.endsWith(' ')) {
const range = new vscode.Range(
new vscode.Position(position.line, 0),
new vscode.Position(position.line, line.text.length)
);
return [vscode.TextEdit.replace(range, trimmedLine)];
}
return [];
}
// 格式化代码块
private formatCodeBlock(
document: vscode.TextDocument,
position: vscode.Position,
options: vscode.FormattingOptions
): vscode.TextEdit[] {
const edits: vscode.TextEdit[] = [];
const line = document.lineAt(position.line);
// 查找匹配的左大括号
let braceCount = 1;
let blockStartLine = -1;
for (let i = position.line - 1; i >= 0; i--) {
const currentLine = document.lineAt(i);
const lineText = currentLine.text;
for (let j = lineText.length - 1; j >= 0; j--) {
if (lineText[j] === '}') {
braceCount++;
} else if (lineText[j] === '{') {
braceCount--;
if (braceCount === 0) {
blockStartLine = i;
break;
}
}
}
if (blockStartLine !== -1) {
break;
}
}
if (blockStartLine !== -1) {
// 调整代码块的缩进
const startLine = document.lineAt(blockStartLine);
const startIndent = this.getIndentLevel(startLine.text);
for (let i = blockStartLine + 1; i < position.line; i++) {
const currentLine = document.lineAt(i);
const currentIndent = this.getIndentLevel(currentLine.text);
if (currentIndent < startIndent + 1) {
const newIndent = this.createIndent(startIndent + 1, options);
const range = new vscode.Range(
new vscode.Position(i, 0),
new vscode.Position(i, currentIndent)
);
edits.push(vscode.TextEdit.replace(range, newIndent));
}
}
// 调整右大括号的缩进
const endIndent = this.createIndent(startIndent, options);
const endRange = new vscode.Range(
new vscode.Position(position.line, 0),
new vscode.Position(position.line, this.getIndentLevel(line.text))
);
edits.push(vscode.TextEdit.replace(endRange, endIndent));
}
return edits;
}
// 获取行的缩进级别
private getIndentLevel(line: string): number {
let indent = 0;
for (let i = 0; i < line.length; i++) {
if (line[i] === ' ') {
indent++;
} else if (line[i] === '\t') {
indent += 4; // 假设一个tab等于4个空格
} else {
break;
}
}
return indent;
}
// 创建指定级别的缩进
private createIndent(level: number, options: vscode.FormattingOptions): string {
if (options.insertSpaces) {
return ' '.repeat(level);
} else {
return '\t'.repeat(Math.floor(level / 4)); // 假设一个tab等于4个空格
}
}
}

View File

@@ -0,0 +1,151 @@
import * as vscode from 'vscode';
import { FunctionCacheManager, FunctionInfo } from '../functionExtractor';
import { ApiParser, ApiFunction } from './apiParser';
// 函数签名信息接口
export interface FunctionSignature {
label: string;
documentation?: string;
parameters: {
label: string;
documentation?: string;
}[];
}
// 签名帮助提供者类
export class SignatureHelpProvider implements vscode.SignatureHelpProvider {
private cacheManager: FunctionCacheManager;
private apiParser: ApiParser;
constructor(cacheManager: FunctionCacheManager) {
this.cacheManager = cacheManager;
this.apiParser = ApiParser.getInstance();
}
// 提供函数调用时的参数信息和帮助
async provideSignatureHelp(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
context: vscode.SignatureHelpContext
): Promise<vscode.SignatureHelp | undefined> {
// 获取光标前的文本
const textBeforeCursor = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
// 查找最近的未闭合的函数调用
const functionCall = this.findCurrentFunctionCall(textBeforeCursor);
if (!functionCall) {
return undefined;
}
// 首先检查是否是API函数
const apiFunction = this.apiParser.getFunctionByName(functionCall.functionName);
if (apiFunction) {
// 创建签名帮助对象
const signatureHelp = new vscode.SignatureHelp();
signatureHelp.signatures = [this.createApiSignatureInformation(apiFunction)];
signatureHelp.activeSignature = 0;
signatureHelp.activeParameter = functionCall.currentParameterIndex;
return signatureHelp;
}
// 查找自定义函数信息
const functions = this.cacheManager.findFunctionsByName(functionCall.functionName);
if (functions.length === 0) {
return undefined;
}
// 创建签名帮助对象
const signatureHelp = new vscode.SignatureHelp();
signatureHelp.signatures = functions.map(func => this.createSignatureInformation(func));
signatureHelp.activeSignature = 0;
signatureHelp.activeParameter = functionCall.currentParameterIndex;
return signatureHelp;
}
// 查找当前的函数调用
private findCurrentFunctionCall(text: string): { functionName: string; currentParameterIndex: number } | undefined {
// 简化的实现,实际项目中可能需要更复杂的解析
// 查找最近的开括号
let openParenIndex = -1;
let parenCount = 0;
for (let i = text.length - 1; i >= 0; i--) {
const char = text[i];
if (char === ')') {
parenCount++;
} else if (char === '(') {
if (parenCount === 0) {
openParenIndex = i;
break;
}
parenCount--;
}
}
if (openParenIndex === -1) {
return undefined;
}
// 查找函数名
const beforeParen = text.substring(0, openParenIndex).trim();
const lastSpaceIndex = beforeParen.lastIndexOf(' ');
const functionName = lastSpaceIndex === -1 ? beforeParen : beforeParen.substring(lastSpaceIndex + 1);
// 计算当前参数索引
let currentParameterIndex = 0;
let commaCount = 0;
for (let i = openParenIndex + 1; i < text.length; i++) {
const char = text[i];
if (char === ',') {
commaCount++;
} else if (char === ')') {
break;
}
}
currentParameterIndex = commaCount;
return {
functionName: functionName,
currentParameterIndex: currentParameterIndex
};
}
// 创建签名信息
private createSignatureInformation(func: FunctionInfo): vscode.SignatureInformation {
const signatureInfo = new vscode.SignatureInformation(func.signature);
signatureInfo.documentation = new vscode.MarkdownString(`定义于文件: ${func.filePath}`);
// 解析参数信息
const paramMatch = func.signature.match(/\(([^)]*)\)/);
if (paramMatch && paramMatch[1]) {
const params = paramMatch[1].split(',').map(p => p.trim()).filter(p => p.length > 0);
signatureInfo.parameters = params.map(param => {
const paramInfo = new vscode.ParameterInformation(param);
paramInfo.documentation = `参数: ${param}`;
return paramInfo;
});
}
return signatureInfo;
}
// 创建API签名信息
private createApiSignatureInformation(apiFunc: ApiFunction): vscode.SignatureInformation {
const signature = this.apiParser.generateFunctionSignature(apiFunc);
const signatureInfo = new vscode.SignatureInformation(signature);
signatureInfo.documentation = new vscode.MarkdownString(apiFunc.description);
// 添加参数信息
signatureInfo.parameters = apiFunc.params.map(param => {
const label = param.optional ? `[${param.name}]` : param.name;
const paramInfo = new vscode.ParameterInformation(label);
paramInfo.documentation = new vscode.MarkdownString(`${param.type} - ${param.description}`);
return paramInfo;
});
return signatureInfo;
}
}