1.0.0 初始版
This commit is contained in:
194
dist/apiClient.js
vendored
Normal file
194
dist/apiClient.js
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiClient = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const http = __importStar(require("http"));
|
||||
const https = __importStar(require("https"));
|
||||
class ApiClient {
|
||||
constructor(baseUrl = 'http://localhost', port = 8080) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.port = port;
|
||||
}
|
||||
getFullUrl(endpoint) {
|
||||
return `${this.baseUrl}:${this.port}${endpoint}`;
|
||||
}
|
||||
// 通用的 HTTP 请求方法
|
||||
async request(url, method = 'GET', body, headers = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
const isHttps = urlObj.protocol === 'https:';
|
||||
const client = isHttps ? https : http;
|
||||
const options = {
|
||||
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 = 'sqr', fileType = 'nut') {
|
||||
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);
|
||||
if (result.IsError) {
|
||||
throw new Error(result.Msg || '获取文件列表失败');
|
||||
}
|
||||
return result.Data;
|
||||
}
|
||||
catch (error) {
|
||||
vscode.window.showErrorMessage(`获取文件列表失败: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// 批量获取文件内容
|
||||
async getFileContents(filePaths, encodingType = null) {
|
||||
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'
|
||||
});
|
||||
if (result.IsError) {
|
||||
throw new Error(result.Msg || '获取文件内容失败');
|
||||
}
|
||||
return result.Data.FileContentData;
|
||||
}
|
||||
catch (error) {
|
||||
vscode.window.showErrorMessage(`获取文件内容失败: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// 上传文件内容
|
||||
async uploadFileContent(filePath, content) {
|
||||
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() {
|
||||
try {
|
||||
const url = this.getFullUrl('/Api/PvfUtiltiy/getVersion');
|
||||
console.log(`版本检查URL: ${url}`);
|
||||
const result = await this.request(url);
|
||||
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, port) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
exports.ApiClient = ApiClient;
|
||||
//# sourceMappingURL=apiClient.js.map
|
||||
1
dist/apiClient.js.map
vendored
Normal file
1
dist/apiClient.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
133
dist/commands.js
vendored
Normal file
133
dist/commands.js
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.registerCommands = registerCommands;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
function registerCommands(context, model, provider, functionExtractor, output) {
|
||||
// 连接到 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) => {
|
||||
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);
|
||||
}
|
||||
//# sourceMappingURL=commands.js.map
|
||||
1
dist/commands.js.map
vendored
Normal file
1
dist/commands.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
||||
151
dist/extension.js
vendored
Normal file
151
dist/extension.js
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.activate = activate;
|
||||
exports.deactivate = deactivate;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const model_1 = require("./model");
|
||||
const provider_1 = require("./provider");
|
||||
const fileSystemProvider_1 = require("./fileSystemProvider");
|
||||
const commands_1 = require("./commands");
|
||||
const functionExtractor_1 = require("./functionExtractor");
|
||||
const completionProvider_1 = require("./providers/completionProvider");
|
||||
const hoverProvider_1 = require("./providers/hoverProvider");
|
||||
const definitionProvider_1 = require("./providers/definitionProvider");
|
||||
const signatureHelpProvider_1 = require("./providers/signatureHelpProvider");
|
||||
const onTypeFormattingProvider_1 = require("./providers/onTypeFormattingProvider");
|
||||
const documentFormattingProvider_1 = require("./providers/documentFormattingProvider");
|
||||
const codeErrorProvider_1 = require("./providers/codeErrorProvider");
|
||||
function activate(context) {
|
||||
console.log('Squirrel NUT Explorer 正在激活...');
|
||||
// 创建模型和提供者
|
||||
const model = new model_1.FileModel();
|
||||
const provider = new provider_1.FileProvider(model);
|
||||
const functionExtractor = new functionExtractor_1.FunctionExtractor();
|
||||
const output = vscode.window.createOutputChannel('Squirrel');
|
||||
// 创建自动完成提供者
|
||||
const completionProvider = new completionProvider_1.CompletionProvider(functionExtractor.getCacheManager());
|
||||
const dotCompletionProvider = new completionProvider_1.DotCompletionProvider();
|
||||
// 创建悬停信息提供者
|
||||
const hoverProvider = new hoverProvider_1.HoverProvider(functionExtractor.getCacheManager());
|
||||
// 创建定义跳转提供者
|
||||
const definitionProvider = new definitionProvider_1.DefinitionProvider(functionExtractor.getCacheManager());
|
||||
// 创建签名帮助提供者
|
||||
const signatureHelpProvider = new signatureHelpProvider_1.SignatureHelpProvider(functionExtractor.getCacheManager());
|
||||
// 创建输入时格式化提供者
|
||||
const onTypeFormattingProvider = new onTypeFormattingProvider_1.OnTypeFormattingProvider();
|
||||
// 创建文档格式化提供者
|
||||
const documentFormattingProvider = new documentFormattingProvider_1.DocumentFormattingProvider();
|
||||
// 创建代码错误检测提供者
|
||||
const codeErrorProvider = new codeErrorProvider_1.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_1.CodeErrorProvider.provideDiagnostics(event.document, diagnosticCollection);
|
||||
}
|
||||
}), vscode.window.onDidChangeActiveTextEditor(editor => {
|
||||
if (editor && editor.document.languageId === 'squirrel') {
|
||||
codeErrorProvider_1.CodeErrorProvider.provideDiagnostics(editor.document, diagnosticCollection);
|
||||
}
|
||||
}), vscode.workspace.onDidOpenTextDocument(document => {
|
||||
if (document.languageId === 'squirrel') {
|
||||
codeErrorProvider_1.CodeErrorProvider.provideDiagnostics(document, diagnosticCollection);
|
||||
}
|
||||
}));
|
||||
// 对所有已打开的squirrel文档进行初始诊断
|
||||
vscode.workspace.textDocuments.forEach(document => {
|
||||
if (document.languageId === 'squirrel') {
|
||||
codeErrorProvider_1.CodeErrorProvider.provideDiagnostics(document, diagnosticCollection);
|
||||
}
|
||||
});
|
||||
// 注册文件系统提供者
|
||||
const fileSystemProvider = new fileSystemProvider_1.SquirrelFileSystemProvider(model);
|
||||
vscode.workspace.registerFileSystemProvider('squirrel', fileSystemProvider, {
|
||||
isCaseSensitive: true
|
||||
});
|
||||
// 注册所有命令
|
||||
console.log('正在注册命令...');
|
||||
(0, commands_1.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"开始使用');
|
||||
}
|
||||
function deactivate() {
|
||||
console.log('Squirrel NUT Explorer 正在停用...');
|
||||
}
|
||||
//# sourceMappingURL=extension.js.map
|
||||
1
dist/extension.js.map
vendored
Normal file
1
dist/extension.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA,4BA8IC;AAED,gCAEC;AAhKD,+CAAiC;AACjC,mCAAoC;AACpC,yCAA0C;AAC1C,6DAAkE;AAClE,yCAA8C;AAC9C,2DAAwD;AACxD,uEAA2F;AAC3F,6DAA0D;AAC1D,uEAAoE;AACpE,6EAA0E;AAC1E,mFAAgF;AAChF,uFAAoF;AACpF,qEAAkE;AAElE,SAAgB,QAAQ,CAAC,OAAgC;IACrD,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;IAE7C,WAAW;IACX,MAAM,KAAK,GAAG,IAAI,iBAAS,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,uBAAY,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,iBAAiB,GAAG,IAAI,qCAAiB,EAAE,CAAC;IAClD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAE7D,YAAY;IACZ,MAAM,kBAAkB,GAAG,IAAI,uCAAkB,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC,CAAC;IACvF,MAAM,qBAAqB,GAAG,IAAI,0CAAqB,EAAE,CAAC;IAE1D,YAAY;IACZ,MAAM,aAAa,GAAG,IAAI,6BAAa,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC,CAAC;IAE7E,YAAY;IACZ,MAAM,kBAAkB,GAAG,IAAI,uCAAkB,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC,CAAC;IAEvF,YAAY;IACZ,MAAM,qBAAqB,GAAG,IAAI,6CAAqB,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC,CAAC;IAE7F,cAAc;IACd,MAAM,wBAAwB,GAAG,IAAI,mDAAwB,EAAE,CAAC;IAEhE,aAAa;IACb,MAAM,0BAA0B,GAAG,IAAI,uDAA0B,EAAE,CAAC;IAEpE,cAAc;IACd,MAAM,iBAAiB,GAAG,IAAI,qCAAiB,EAAE,CAAC;IAClD,MAAM,oBAAoB,GAAG,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAErF,YAAY;IACZ,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC;IAEzE,YAAY;IACZ,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,8BAA8B,CAAC,UAAU,EAAE,kBAAkB,EAAE,EAAE,CAAC,EACnF,MAAM,CAAC,SAAS,CAAC,8BAA8B,CAAC,UAAU,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAC1F,CAAC;IAEF,YAAY;IACZ,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,UAAU,EAAE,aAAa,CAAC,CACpE,CAAC;IAEF,YAAY;IACZ,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAC9E,CAAC;IAEF,YAAY;IACZ,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,6BAA6B,CAAC,UAAU,EAAE,qBAAqB,EAAE,GAAG,EAAE,GAAG,CAAC,CAC9F,CAAC;IAEF,cAAc;IACd,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,oCAAoC,CAAC,UAAU,EAAE,wBAAwB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAC7G,CAAC;IAEF,aAAa;IACb,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,sCAAsC,CAAC,UAAU,EAAE,0BAA0B,CAAC,CAClG,CAAC;IAEF,cAAc;IACd,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,2BAA2B,CAAC,UAAU,EAAE,iBAAiB,CAAC,EAC3E,oBAAoB,CACvB,CAAC;IAEF,YAAY;IACZ,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAC,KAAK,CAAC,EAAE;QAC7C,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC3C,qCAAiB,CAAC,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;QAC/E,CAAC;IACL,CAAC,CAAC,EACF,MAAM,CAAC,MAAM,CAAC,2BAA2B,CAAC,MAAM,CAAC,EAAE;QAC/C,IAAI,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACtD,qCAAiB,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;QAChF,CAAC;IACL,CAAC,CAAC,EACF,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE;QAC9C,IAAI,QAAQ,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACrC,qCAAiB,CAAC,kBAAkB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;QACzE,CAAC;IACL,CAAC,CAAC,CACL,CAAC;IAEF,0BAA0B;IAC1B,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;QAC9C,IAAI,QAAQ,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACrC,qCAAiB,CAAC,kBAAkB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;QACzE,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,YAAY;IACZ,MAAM,kBAAkB,GAAG,IAAI,+CAA0B,CAAC,KAAK,CAAC,CAAC;IACjE,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,UAAU,EAAE,kBAAkB,EAAE;QACxE,eAAe,EAAE,IAAI;KACxB,CAAC,CAAC;IAEH,SAAS;IACT,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACzB,IAAA,2BAAgB,EAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtB,cAAc;IACd,MAAM,oBAAoB,GAAG,MAAM,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE,EAAE;QACzE,IAAI,CAAC,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC;YACrC,KAAK,CAAC,YAAY,EAAE,CAAC;YACrB,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;QAC1C,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,WAAW;IACX,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtE,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;YACxD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAErE,IAAI,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,UAAU,CAAC,iBAAiB,QAAQ,OAAO,CAAC,CAAC;gBACpD,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAEnB,SAAS;gBACT,MAAM,iBAAiB,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,UAAU,CAAC,iBAAiB,QAAQ,OAAO,CAAC,CAAC;YACxD,CAAC;QACL,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,oBAAoB,EAAE,YAAY,CAAC,CAAC;IAE/D,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;IACvC,MAAM,CAAC,IAAI,EAAE,CAAC;IAEd,SAAS;IACT,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,uDAAuD,CAAC,CAAC;AAClG,CAAC;AAED,SAAgB,UAAU;IACtB,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;AACjD,CAAC"}
|
||||
95
dist/fileSystemProvider.js
vendored
Normal file
95
dist/fileSystemProvider.js
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SquirrelFileSystemProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
class SquirrelFileSystemProvider {
|
||||
constructor(model) {
|
||||
this._onDidChangeFile = new vscode.EventEmitter();
|
||||
this.onDidChangeFile = this._onDidChangeFile.event;
|
||||
this.model = model;
|
||||
}
|
||||
// 读取文件
|
||||
async readFile(uri) {
|
||||
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, content, options) {
|
||||
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) {
|
||||
return {
|
||||
type: vscode.FileType.File,
|
||||
ctime: Date.now(),
|
||||
mtime: Date.now(),
|
||||
size: 0
|
||||
};
|
||||
}
|
||||
readDirectory(uri) {
|
||||
return [];
|
||||
}
|
||||
createDirectory(uri) {
|
||||
throw vscode.FileSystemError.NoPermissions('不支持创建目录');
|
||||
}
|
||||
delete(uri) {
|
||||
throw vscode.FileSystemError.NoPermissions('不支持删除文件');
|
||||
}
|
||||
rename(oldUri, newUri, options) {
|
||||
throw vscode.FileSystemError.NoPermissions('不支持重命名文件');
|
||||
}
|
||||
watch(uri, options) {
|
||||
return new vscode.Disposable(() => { });
|
||||
}
|
||||
// 从 URI 解析文件路径
|
||||
getPathFromUri(uri) {
|
||||
if (uri.scheme !== 'squirrel') {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
}
|
||||
return uri.path.substring(1); // 去掉开头的 '/'
|
||||
}
|
||||
}
|
||||
exports.SquirrelFileSystemProvider = SquirrelFileSystemProvider;
|
||||
//# sourceMappingURL=fileSystemProvider.js.map
|
||||
1
dist/fileSystemProvider.js.map
vendored
Normal file
1
dist/fileSystemProvider.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"fileSystemProvider.js","sourceRoot":"","sources":["../src/fileSystemProvider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAGjC,MAAa,0BAA0B;IAKnC,YAAY,KAAgB;QAHpB,qBAAgB,GAAG,IAAI,MAAM,CAAC,YAAY,EAA4B,CAAC;QAC/E,oBAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC;QAG1C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,OAAO;IACP,KAAK,CAAC,QAAQ,CAAC,GAAe;QAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAE1D,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO;IACP,KAAK,CAAC,SAAS,CAAC,GAAe,EAAE,OAAmB,EAAE,OAAgD;QAClG,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEtD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAExE,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACvD,CAAC;IACL,CAAC;IAED,kBAAkB;IAClB,IAAI,CAAC,GAAe;QAChB,OAAO;YACH,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI;YAC1B,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE;YACjB,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE;YACjB,IAAI,EAAE,CAAC;SACV,CAAC;IACN,CAAC;IAED,aAAa,CAAC,GAAe;QACzB,OAAO,EAAE,CAAC;IACd,CAAC;IAED,eAAe,CAAC,GAAe;QAC3B,MAAM,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,GAAe;QAClB,MAAM,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,MAAkB,EAAE,MAAkB,EAAE,OAA+B;QAC1E,MAAM,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,GAAe,EAAE,OAAmD;QACtE,OAAO,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,eAAe;IACP,cAAc,CAAC,GAAe;QAClC,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC5B,MAAM,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;IAC9C,CAAC;CACJ;AAtED,gEAsEC"}
|
||||
264
dist/functionExtractor.js
vendored
Normal file
264
dist/functionExtractor.js
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.FunctionExtractor = exports.FunctionCacheManager = exports.PerformanceMonitor = void 0;
|
||||
// 性能监控类
|
||||
class PerformanceMonitor {
|
||||
constructor() {
|
||||
this.extractionTimes = [];
|
||||
this.fileCounts = [];
|
||||
this.functionCounts = [];
|
||||
}
|
||||
// 记录函数提取性能
|
||||
recordExtraction(timeMs, fileCount, functionCount) {
|
||||
this.extractionTimes.push(timeMs);
|
||||
this.fileCounts.push(fileCount);
|
||||
this.functionCounts.push(functionCount);
|
||||
}
|
||||
// 获取平均提取时间
|
||||
getAverageExtractionTime() {
|
||||
if (this.extractionTimes.length === 0)
|
||||
return 0;
|
||||
const sum = this.extractionTimes.reduce((a, b) => a + b, 0);
|
||||
return sum / this.extractionTimes.length;
|
||||
}
|
||||
// 获取平均每文件处理时间
|
||||
getAverageTimePerFile() {
|
||||
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() {
|
||||
return {
|
||||
averageExtractionTime: this.getAverageExtractionTime(),
|
||||
averageTimePerFile: this.getAverageTimePerFile(),
|
||||
totalExtractions: this.extractionTimes.length
|
||||
};
|
||||
}
|
||||
// 清除统计数据
|
||||
clearStats() {
|
||||
this.extractionTimes = [];
|
||||
this.fileCounts = [];
|
||||
this.functionCounts = [];
|
||||
}
|
||||
}
|
||||
exports.PerformanceMonitor = PerformanceMonitor;
|
||||
// 函数缓存管理类
|
||||
class FunctionCacheManager {
|
||||
constructor() {
|
||||
this.functionCache = new Map();
|
||||
this.fileProcessed = new Set();
|
||||
}
|
||||
// 添加函数信息到缓存
|
||||
addFunctions(filePath, functions) {
|
||||
this.functionCache.set(filePath, functions);
|
||||
this.fileProcessed.add(filePath);
|
||||
}
|
||||
// 获取文件的函数信息
|
||||
getFunctions(filePath) {
|
||||
return this.functionCache.get(filePath);
|
||||
}
|
||||
// 检查文件是否已处理
|
||||
isFileProcessed(filePath) {
|
||||
return this.fileProcessed.has(filePath);
|
||||
}
|
||||
// 清除特定文件的缓存
|
||||
clearFileCache(filePath) {
|
||||
this.functionCache.delete(filePath);
|
||||
this.fileProcessed.delete(filePath);
|
||||
}
|
||||
// 清除所有缓存
|
||||
clearAllCache() {
|
||||
this.functionCache.clear();
|
||||
this.fileProcessed.clear();
|
||||
}
|
||||
// 获取缓存统计信息
|
||||
getCacheStats() {
|
||||
let functionCount = 0;
|
||||
for (const functions of this.functionCache.values()) {
|
||||
functionCount += functions.length;
|
||||
}
|
||||
return {
|
||||
fileCount: this.functionCache.size,
|
||||
functionCount: functionCount
|
||||
};
|
||||
}
|
||||
// 获取所有缓存的函数信息
|
||||
getAllFunctions() {
|
||||
const allFunctions = [];
|
||||
for (const functions of this.functionCache.values()) {
|
||||
allFunctions.push(...functions);
|
||||
}
|
||||
return allFunctions;
|
||||
}
|
||||
// 根据函数名称查找函数信息
|
||||
findFunctionsByName(functionName) {
|
||||
const allFunctions = this.getAllFunctions();
|
||||
return allFunctions.filter(func => func.name === functionName);
|
||||
}
|
||||
// 根据文件路径查找函数信息
|
||||
findFunctionsByFilePath(filePath) {
|
||||
return this.getFunctions(filePath) || [];
|
||||
}
|
||||
}
|
||||
exports.FunctionCacheManager = FunctionCacheManager;
|
||||
// 函数提取器类
|
||||
class FunctionExtractor {
|
||||
constructor() {
|
||||
this.isExtracting = false; // 防止重复提取
|
||||
this.cacheManager = new FunctionCacheManager();
|
||||
this.performanceMonitor = new PerformanceMonitor();
|
||||
}
|
||||
// 获取缓存管理器
|
||||
getCacheManager() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
// 获取性能监控器
|
||||
getPerformanceMonitor() {
|
||||
return this.performanceMonitor;
|
||||
}
|
||||
// 检查是否正在提取函数
|
||||
isExtractingFunctions() {
|
||||
return this.isExtracting;
|
||||
}
|
||||
// 从文件内容中提取函数信息
|
||||
extractFunctionsFromFile(filePath, content) {
|
||||
const functions = [];
|
||||
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 = {
|
||||
name: functionName,
|
||||
filePath: filePath,
|
||||
lineNumber: i + 1,
|
||||
parameters: parameters,
|
||||
signature: `function ${functionName}(${paramsString})`
|
||||
};
|
||||
functions.push(functionInfo);
|
||||
}
|
||||
}
|
||||
return functions;
|
||||
}
|
||||
// 解析参数字符串
|
||||
parseParameters(paramsString) {
|
||||
if (!paramsString.trim()) {
|
||||
return [];
|
||||
}
|
||||
// 简单参数解析,按逗号分割
|
||||
return paramsString.split(',').map(param => param.trim()).filter(param => param.length > 0);
|
||||
}
|
||||
// 批量提取函数信息
|
||||
async extractFunctionsFromFiles(model, filePaths) {
|
||||
// 防止重复提取
|
||||
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) {
|
||||
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, filePath) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.FunctionExtractor = FunctionExtractor;
|
||||
//# sourceMappingURL=functionExtractor.js.map
|
||||
1
dist/functionExtractor.js.map
vendored
Normal file
1
dist/functionExtractor.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
283
dist/model.js
vendored
Normal file
283
dist/model.js
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.FileModel = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const apiClient_1 = require("./apiClient");
|
||||
class FileModel {
|
||||
constructor() {
|
||||
this.fileList = new Map();
|
||||
this.isConnected = false;
|
||||
this.apiClient = new apiClient_1.ApiClient();
|
||||
this.config = vscode.workspace.getConfiguration('squirrel');
|
||||
}
|
||||
// 更新配置
|
||||
updateConfig() {
|
||||
this.config = vscode.workspace.getConfiguration('squirrel');
|
||||
const baseUrl = this.config.get('api.baseUrl', 'http://localhost');
|
||||
const port = this.config.get('api.port', 8080);
|
||||
this.apiClient.updateConfig(baseUrl, port);
|
||||
}
|
||||
// 连接到API
|
||||
async connect() {
|
||||
try {
|
||||
this.updateConfig();
|
||||
const baseUrl = this.config.get('api.baseUrl', 'http://localhost');
|
||||
const port = this.config.get('api.port', 8080);
|
||||
const defaultDir = this.config.get('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) => file.trim() !== '');
|
||||
console.log(`成功获取到 ${files.length} 个文件`);
|
||||
this.fileList.clear();
|
||||
files.forEach((filePath) => {
|
||||
const entry = {
|
||||
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() {
|
||||
return this.isConnected;
|
||||
}
|
||||
// 获取指定目录的子文件(支持树形结构)
|
||||
getChildren(parentKey) {
|
||||
if (!parentKey) {
|
||||
// 获取根目录内容
|
||||
const folders = new Map(); // folderKey -> name
|
||||
const files = [];
|
||||
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 = [...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();
|
||||
const result = [];
|
||||
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) {
|
||||
const entry = this.fileList.get(filePath);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
// 如果已经加载过内容,直接返回
|
||||
if (entry.content) {
|
||||
return entry.content;
|
||||
}
|
||||
try {
|
||||
// 获取编码设置
|
||||
const encodingType = this.config.get('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, content) {
|
||||
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() {
|
||||
if (!this.isConnected) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
this.updateConfig();
|
||||
const defaultDir = this.config.get('defaultDirectory', 'sqr');
|
||||
const filesData = await this.apiClient.getFileList(defaultDir);
|
||||
// 将字符串按换行符分割成数组
|
||||
const files = filesData.split(/\r?\n/).filter((file) => file.trim() !== '');
|
||||
this.fileList.clear();
|
||||
files.forEach((filePath) => {
|
||||
const entry = {
|
||||
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() {
|
||||
try {
|
||||
// 获取所有文件路径
|
||||
const filePaths = Array.from(this.fileList.keys());
|
||||
if (filePaths.length === 0) {
|
||||
console.log('没有文件需要加载');
|
||||
return;
|
||||
}
|
||||
console.log(`准备加载 ${filePaths.length} 个文件的内容`);
|
||||
// 获取编码设置
|
||||
const encodingType = this.config.get('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) {
|
||||
return this.fileList.get(filePath);
|
||||
}
|
||||
// 获取所有nut文件路径
|
||||
getAllNutFiles() {
|
||||
return Array.from(this.fileList.keys())
|
||||
.filter(filePath => filePath.endsWith('.nut'));
|
||||
}
|
||||
}
|
||||
exports.FileModel = FileModel;
|
||||
//# sourceMappingURL=model.js.map
|
||||
1
dist/model.js.map
vendored
Normal file
1
dist/model.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
100
dist/provider.js
vendored
Normal file
100
dist/provider.js
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.FileProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
class FileProvider {
|
||||
constructor(model, output) {
|
||||
this.model = model;
|
||||
this.output = output;
|
||||
this._onDidChangeTreeData = new vscode.EventEmitter();
|
||||
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||||
}
|
||||
refresh() {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
getTreeItem(element) {
|
||||
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) {
|
||||
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) {
|
||||
// 简单实现,返回 undefined 表示根目录
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
exports.FileProvider = FileProvider;
|
||||
//# sourceMappingURL=provider.js.map
|
||||
1
dist/provider.js.map
vendored
Normal file
1
dist/provider.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAGjC,MAAa,YAAY;IAIrB,YAAoB,KAAgB,EAAU,MAA6B;QAAvD,UAAK,GAAL,KAAK,CAAW;QAAU,WAAM,GAAN,MAAM,CAAuB;QAHnE,yBAAoB,GAAG,IAAI,MAAM,CAAC,YAAY,EAAgC,CAAC;QACvF,wBAAmB,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC;IAEwB,CAAC;IAE/E,OAAO;QACH,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC;IAED,WAAW,CAAC,OAAkB;QAC1B,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,QAAQ,CAC5B,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,SAAS,CACpG,CAAC;QAEF,gBAAgB;QAChB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAiB,CAAC;QAEzE,aAAa;QACb,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QAEvB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACjB,SAAS;YACT,IAAI,CAAC,QAAQ,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YAElD,YAAY;YACZ,IAAI,CAAC,OAAO,GAAG;gBACX,OAAO,EAAE,mBAAmB;gBAC5B,KAAK,EAAE,IAAI;gBACX,SAAS,EAAE,CAAC,OAAO,CAAC;aACvB,CAAC;YAEF,aAAa;YACb,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YAC7B,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,QAAQ;YACR,IAAI,CAAC,QAAQ,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,WAAW,CAAC,OAAmB;QAC3B,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAEpD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC;gBACtE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,iBAAiB,KAAK,OAAO,MAAM,CAAC,MAAM,QAAQ,CAAC,CAAC;YAC/E,CAAC;YAED,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,sCAAsC,KAAK,EAAE,CAAC,CAAC;YAC1E,CAAC;YACD,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;IACL,CAAC;IAED,SAAS,CAAE,OAAkB;QACzB,0BAA0B;QAC1B,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;CACJ;AAtED,oCAsEC"}
|
||||
389
dist/providers/apiParser.js
vendored
Normal file
389
dist/providers/apiParser.js
vendored
Normal file
@@ -0,0 +1,389 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiParser = void 0;
|
||||
// API文档解析器类
|
||||
class ApiParser {
|
||||
constructor() {
|
||||
this.functions = [];
|
||||
this.classes = [];
|
||||
this.constants = [];
|
||||
this.initializeApiDocumentation();
|
||||
}
|
||||
// 获取单例实例
|
||||
static getInstance() {
|
||||
if (!ApiParser.instance) {
|
||||
ApiParser.instance = new ApiParser();
|
||||
}
|
||||
return ApiParser.instance;
|
||||
}
|
||||
// 初始化API文档
|
||||
initializeApiDocumentation() {
|
||||
// 初始化内置函数
|
||||
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'
|
||||
}
|
||||
];
|
||||
}
|
||||
// 获取所有函数
|
||||
getFunctions() {
|
||||
return this.functions;
|
||||
}
|
||||
// 根据名称获取函数
|
||||
getFunctionByName(name) {
|
||||
return this.functions.find(func => func.name === name);
|
||||
}
|
||||
// 获取所有类
|
||||
getClasses() {
|
||||
return this.classes;
|
||||
}
|
||||
// 根据名称获取类
|
||||
getClassByName(name) {
|
||||
return this.classes.find(cls => cls.name === name);
|
||||
}
|
||||
// 获取所有常量
|
||||
getConstants() {
|
||||
return this.constants;
|
||||
}
|
||||
// 根据类别获取常量
|
||||
getConstantsByCategory(category) {
|
||||
return this.constants.filter(constant => constant.category === category);
|
||||
}
|
||||
// 根据名称获取常量
|
||||
getConstantByName(name) {
|
||||
return this.constants.find(constant => constant.name === name);
|
||||
}
|
||||
// 生成函数签名
|
||||
generateFunctionSignature(func) {
|
||||
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'}`;
|
||||
}
|
||||
// 生成类签名
|
||||
generateClassSignature(cls) {
|
||||
return `class ${cls.name}`;
|
||||
}
|
||||
// 生成属性签名
|
||||
generatePropertySignature(prop) {
|
||||
return `property ${prop.name}: ${prop.type}`;
|
||||
}
|
||||
// 生成方法签名
|
||||
generateMethodSignature(method) {
|
||||
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'}`;
|
||||
}
|
||||
}
|
||||
exports.ApiParser = ApiParser;
|
||||
//# sourceMappingURL=apiParser.js.map
|
||||
1
dist/providers/apiParser.js.map
vendored
Normal file
1
dist/providers/apiParser.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
906
dist/providers/codeErrorProvider.js
vendored
Normal file
906
dist/providers/codeErrorProvider.js
vendored
Normal file
@@ -0,0 +1,906 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CodeErrorProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
// 代码错误检测提供者类
|
||||
class CodeErrorProvider {
|
||||
// 提供代码诊断信息
|
||||
static async provideDiagnostics(document, diagnostics) {
|
||||
const diagnosticList = [];
|
||||
let isInBlockComment = false; // 文档级别的块注释状态
|
||||
// 全文档检查,支持多行结构
|
||||
const documentText = document.getText();
|
||||
const lines = documentText.split('\n');
|
||||
// 检查未闭合的字符串(支持多行)
|
||||
diagnosticList.push(...this.checkForUnclosedStrings(document));
|
||||
// 检查未闭合的括号(支持多行)
|
||||
diagnosticList.push(...this.checkForUnclosedBrackets(document));
|
||||
// 逐行检查其他错误,传递块注释状态
|
||||
for (let i = 0; i < document.lineCount; i++) {
|
||||
const line = document.lineAt(i);
|
||||
const result = this.checkLineForErrors(line, i, isInBlockComment);
|
||||
diagnosticList.push(...result.diagnostics);
|
||||
isInBlockComment = result.isInBlockComment; // 更新块注释状态
|
||||
}
|
||||
// 发布诊断信息
|
||||
diagnostics.set(document.uri, diagnosticList);
|
||||
}
|
||||
// 检查单行代码错误,支持块注释状态跟踪
|
||||
static checkLineForErrors(line, lineIndex, isInBlockComment) {
|
||||
const diagnostics = [];
|
||||
const lineText = line.text;
|
||||
// 处理行注释和块注释状态
|
||||
const commentInfo = this.processCommentsAndGetValidText(lineText, isInBlockComment);
|
||||
// 如果整行都在注释中,直接返回
|
||||
if (commentInfo.skipEntireLine) {
|
||||
return { diagnostics, isInBlockComment: commentInfo.newBlockCommentState };
|
||||
}
|
||||
// 只对非注释部分进行错误检测
|
||||
if (commentInfo.validText.trim().length > 0) {
|
||||
// 检查常见的语法错误
|
||||
diagnostics.push(...this.checkForCommonSyntaxErrorsWithValidText(lineText, commentInfo.validText, commentInfo.validRange, lineIndex));
|
||||
// 检查可能的拼写错误
|
||||
diagnostics.push(...this.checkForPossibleTyposWithValidText(lineText, commentInfo.validText, commentInfo.validRange, lineIndex));
|
||||
}
|
||||
return {
|
||||
diagnostics,
|
||||
isInBlockComment: commentInfo.newBlockCommentState
|
||||
};
|
||||
}
|
||||
// 处理注释并获取有效的代码文本
|
||||
static processCommentsAndGetValidText(lineText, isInBlockComment) {
|
||||
let currentBlockCommentState = isInBlockComment;
|
||||
// 如果整行都在块注释中,先检查是否有结束标记
|
||||
if (currentBlockCommentState) {
|
||||
const blockCommentEnd = lineText.indexOf('*/');
|
||||
if (blockCommentEnd !== -1) {
|
||||
// 找到块注释结束,从结束位置后开始处理
|
||||
currentBlockCommentState = false;
|
||||
const remainingLine = lineText.substring(blockCommentEnd + 2);
|
||||
// 递归处理剩余部分
|
||||
const remainingResult = this.processCommentsAndGetValidText(remainingLine, false);
|
||||
return {
|
||||
validText: remainingResult.validText,
|
||||
validRange: {
|
||||
start: blockCommentEnd + 2 + remainingResult.validRange.start,
|
||||
end: blockCommentEnd + 2 + remainingResult.validRange.end
|
||||
},
|
||||
skipEntireLine: remainingResult.skipEntireLine,
|
||||
newBlockCommentState: remainingResult.newBlockCommentState
|
||||
};
|
||||
}
|
||||
else {
|
||||
// 整行都在块注释中
|
||||
return {
|
||||
validText: '',
|
||||
validRange: { start: 0, end: 0 },
|
||||
skipEntireLine: true,
|
||||
newBlockCommentState: true
|
||||
};
|
||||
}
|
||||
}
|
||||
// 现在处理这一行,移除所有注释,只保留有效代码部分
|
||||
let result = '';
|
||||
let validRanges = [];
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let escaped = false;
|
||||
let i = 0;
|
||||
let currentValidStart = 0;
|
||||
while (i < lineText.length) {
|
||||
const char = lineText[i];
|
||||
const nextChar = i < lineText.length - 1 ? lineText[i + 1] : '';
|
||||
// 处理字符串状态
|
||||
if (!inString) {
|
||||
if (char === '"' || char === "'") {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (char === stringChar) {
|
||||
inString = false;
|
||||
stringChar = '';
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// 只有在非字符串状态下才检查注释
|
||||
if (!inString) {
|
||||
// 检查行注释 // (优先级最高)
|
||||
if (char === '/' && nextChar === '/') {
|
||||
// 遇到行注释,保存当前有效部分,后续全部跳过
|
||||
if (i > currentValidStart) {
|
||||
validRanges.push({ start: currentValidStart, end: i });
|
||||
result += lineText.substring(currentValidStart, i);
|
||||
}
|
||||
break; // 行注释后面的内容全部跳过
|
||||
}
|
||||
// 检查块注释开始 /*
|
||||
if (char === '/' && nextChar === '*') {
|
||||
// 保存块注释前的有效部分
|
||||
if (i > currentValidStart) {
|
||||
validRanges.push({ start: currentValidStart, end: i });
|
||||
result += lineText.substring(currentValidStart, i);
|
||||
}
|
||||
// 寻找块注释结束
|
||||
const blockCommentEnd = lineText.indexOf('*/', i + 2);
|
||||
if (blockCommentEnd !== -1) {
|
||||
// 同行找到块注释结束,跳过整个块注释
|
||||
i = blockCommentEnd + 2;
|
||||
currentValidStart = i;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// 没有找到结束标记,块注释延续到下一行
|
||||
currentBlockCommentState = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// 添加最后的有效部分
|
||||
if (!currentBlockCommentState && currentValidStart < lineText.length) {
|
||||
validRanges.push({ start: currentValidStart, end: lineText.length });
|
||||
result += lineText.substring(currentValidStart);
|
||||
}
|
||||
// 如果没有有效内容,返回空
|
||||
if (validRanges.length === 0) {
|
||||
return {
|
||||
validText: '',
|
||||
validRange: { start: 0, end: 0 },
|
||||
skipEntireLine: true,
|
||||
newBlockCommentState: currentBlockCommentState
|
||||
};
|
||||
}
|
||||
// 返回第一个有效范围(简化处理,实际使用时会在所有有效范围中检查)
|
||||
return {
|
||||
validText: result,
|
||||
validRange: { start: validRanges[0].start, end: validRanges[validRanges.length - 1].end },
|
||||
skipEntireLine: result.trim().length === 0,
|
||||
newBlockCommentState: currentBlockCommentState
|
||||
};
|
||||
}
|
||||
// 检查指定位置是否在字符串中
|
||||
static isInString(line, position) {
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let escaped = false;
|
||||
for (let i = 0; i < position && i < line.length; i++) {
|
||||
const char = line[i];
|
||||
if (!inString) {
|
||||
if (char === '"' || char === "'") {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (char === stringChar) {
|
||||
inString = false;
|
||||
stringChar = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return inString;
|
||||
}
|
||||
// 基于有效文本范围检查语法错误
|
||||
static checkForCommonSyntaxErrorsWithValidText(originalLine, validText, validRange, lineIndex) {
|
||||
const diagnostics = [];
|
||||
// 跳过空行
|
||||
if (validText.trim().length === 0) {
|
||||
return diagnostics;
|
||||
}
|
||||
// 由于validText可能是多个片段拼接的结果,我们需要直接在原始行中查找
|
||||
// 但只检查那些不在注释和字符串中的位置
|
||||
const typoPatterns = [
|
||||
{ pattern: /\bloacl\b/g, correct: 'local', message: '拼写错误:应该是 "local" 而不是 "loacl"' },
|
||||
{ pattern: /\bfunctoin\b/g, correct: 'function', message: '拼写错误:应该是 "function" 而不是 "functoin"' },
|
||||
{ pattern: /\bretun\b/g, correct: 'return', message: '拼写错误:应该是 "return" 而不是 "retun"' },
|
||||
{ pattern: /\bwhlie\b/g, correct: 'while', message: '拼写错误:应该是 "while" 而不是 "whlie"' },
|
||||
{ pattern: /\belsee\b/g, correct: 'else', message: '拼写错误:应该是 "else" 而不是 "elsee"' },
|
||||
{ pattern: /\bforech\b/g, correct: 'foreach', message: '拼写错误:应该是 "foreach" 而不是 "forech"' },
|
||||
{ pattern: /\bclas\b/g, correct: 'class', message: '拼写错误:应该是 "class" 而不是 "clas"' },
|
||||
{ pattern: /\bbreka\b/g, correct: 'break', message: '拼写错误:应该是 "break" 而不是 "breka"' },
|
||||
{ pattern: /\bcontineu\b/g, correct: 'continue', message: '拼写错误:应该是 "continue" 而不是 "contineu"' }
|
||||
];
|
||||
typoPatterns.forEach(({ pattern, correct, message }) => {
|
||||
let match;
|
||||
// 在原始行中查找所有匹配
|
||||
while ((match = pattern.exec(originalLine)) !== null) {
|
||||
const matchPosition = match.index;
|
||||
// 检查这个位置是否在有效的代码区域(不在注释或字符串中)
|
||||
if (this.isPositionInValidCode(originalLine, matchPosition)) {
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, matchPosition), new vscode.Position(lineIndex, matchPosition + match[0].length));
|
||||
const diagnostic = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error);
|
||||
diagnostic.tags = [vscode.DiagnosticTag.Deprecated];
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
});
|
||||
return diagnostics;
|
||||
}
|
||||
// 检查指定位置是否在有效代码中(不在注释或字符串中)
|
||||
static isPositionInValidCode(line, position) {
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let escaped = false;
|
||||
let inBlockComment = false;
|
||||
for (let i = 0; i < position && i < line.length; i++) {
|
||||
const char = line[i];
|
||||
const nextChar = i < line.length - 1 ? line[i + 1] : '';
|
||||
// 如果在块注释中,检查是否遇到结束标记
|
||||
if (inBlockComment) {
|
||||
if (char === '*' && nextChar === '/') {
|
||||
inBlockComment = false;
|
||||
i++; // 跳过 '/'
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 处理字符串状态
|
||||
if (!inString) {
|
||||
if (char === '"' || char === "'") {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (char === stringChar) {
|
||||
inString = false;
|
||||
stringChar = '';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 只有在非字符串状态下才检查注释
|
||||
if (!inString) {
|
||||
// 检查行注释
|
||||
if (char === '/' && nextChar === '/') {
|
||||
return false; // 位置在行注释中
|
||||
}
|
||||
// 检查块注释开始
|
||||
if (char === '/' && nextChar === '*') {
|
||||
inBlockComment = true;
|
||||
i++; // 跳过 '*'
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果到达目标位置时仍在字符串或块注释中,则该位置无效
|
||||
return !inString && !inBlockComment;
|
||||
}
|
||||
// 基于有效文本范围检查可能的拼写错误
|
||||
static checkForPossibleTyposWithValidText(originalLine, validText, validRange, lineIndex) {
|
||||
const diagnostics = [];
|
||||
// 暂时空实现,可以后续扩展
|
||||
return diagnostics;
|
||||
}
|
||||
// 检查常见的语法错误
|
||||
static checkForCommonSyntaxErrors(lineText, lineIndex) {
|
||||
const diagnostics = [];
|
||||
// 跳过空行
|
||||
const trimmedLine = lineText.trim();
|
||||
if (trimmedLine.length === 0) {
|
||||
return diagnostics;
|
||||
}
|
||||
// 检查是否使用了错误的关键字拼写(仅检查明显的拼写错误)
|
||||
const typoPatterns = [
|
||||
{ pattern: /\bloacl\b/g, correct: 'local', message: '拼写错误:应该是 "local" 而不是 "loacl"' },
|
||||
{ pattern: /\bfunctoin\b/g, correct: 'function', message: '拼写错误:应该是 "function" 而不是 "functoin"' },
|
||||
{ pattern: /\bretun\b/g, correct: 'return', message: '拼写错误:应该是 "return" 而不是 "retun"' },
|
||||
{ pattern: /\bwhlie\b/g, correct: 'while', message: '拼写错误:应该是 "while" 而不是 "whlie"' },
|
||||
{ pattern: /\belsee\b/g, correct: 'else', message: '拼写错误:应该是 "else" 而不是 "elsee"' },
|
||||
{ pattern: /\bforech\b/g, correct: 'foreach', message: '拼写错误:应该是 "foreach" 而不是 "forech"' },
|
||||
{ pattern: /\bclas\b/g, correct: 'class', message: '拼写错误:应该是 "class" 而不是 "clas"' },
|
||||
{ pattern: /\bbreka\b/g, correct: 'break', message: '拼写错误:应该是 "break" 而不是 "breka"' },
|
||||
{ pattern: /\bcontineu\b/g, correct: 'continue', message: '拼写错误:应该是 "continue" 而不是 "contineu"' }
|
||||
];
|
||||
typoPatterns.forEach(({ pattern, correct, message }) => {
|
||||
let match;
|
||||
while ((match = pattern.exec(lineText)) !== null) {
|
||||
// 检查这个匹配是否在注释或字符串中
|
||||
if (this.isPositionInCommentOrString(lineText, match.index)) {
|
||||
continue;
|
||||
}
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, match.index), new vscode.Position(lineIndex, match.index + match[0].length));
|
||||
const diagnostic = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error);
|
||||
// 添加快速修复信息
|
||||
diagnostic.tags = [vscode.DiagnosticTag.Deprecated];
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
});
|
||||
// 检查赋值操作符错误(使用 = 而不是 == 进行比较)
|
||||
// 更精确的正则表达式,避免误报
|
||||
const assignmentPattern = /\b(if|while)\s*\(\s*[^=<>!]*\s=(?!=)\s*[^=][^)]*\)/g;
|
||||
let assignmentMatch;
|
||||
while ((assignmentMatch = assignmentPattern.exec(lineText)) !== null) {
|
||||
// 检查这个匹配是否在注释或字符串中
|
||||
if (this.isPositionInCommentOrString(lineText, assignmentMatch.index)) {
|
||||
continue;
|
||||
}
|
||||
// 排除明显的合法赋值情况
|
||||
const matchText = assignmentMatch[0];
|
||||
// 检查是否是函数调用或其他合法语法
|
||||
if (!matchText.includes('()') && !matchText.includes('new ')) {
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, assignmentMatch.index), new vscode.Position(lineIndex, assignmentMatch.index + assignmentMatch[0].length));
|
||||
const diagnostic = new vscode.Diagnostic(range, '可能的逻辑错误:在条件语句中使用了赋值操作符 "=" 而不是比较操作符 "=="', vscode.DiagnosticSeverity.Warning);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
// 检查常见的语法结构错误
|
||||
// 检查函数定义语法
|
||||
if (lineText.includes('function') && !lineText.match(/function\s+[a-zA-Z_]\w*\s*\(/)) {
|
||||
const functionMatch = lineText.match(/function\s*\(/);
|
||||
if (functionMatch && !this.isPositionInCommentOrString(lineText, functionMatch.index)) {
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, functionMatch.index), new vscode.Position(lineIndex, functionMatch.index + functionMatch[0].length));
|
||||
const diagnostic = new vscode.Diagnostic(range, '语法错误:函数定义缺少函数名', vscode.DiagnosticSeverity.Error);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
// 检查缺少分号的语句(仅对明显需要分号的语句检查)
|
||||
const needsSemicolon = /\b(local|return|break|continue)\s+[^;\/]*[^;\s\/]$/;
|
||||
if (needsSemicolon.test(trimmedLine) && !trimmedLine.endsWith(';') && !trimmedLine.endsWith('{')) {
|
||||
// 确保这不是在注释中
|
||||
const cleanLine = this.removeStringsAndComments(lineText);
|
||||
if (cleanLine.trim()) {
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, lineText.length - 1), new vscode.Position(lineIndex, lineText.length));
|
||||
const diagnostic = new vscode.Diagnostic(range, '可能缺少分号', vscode.DiagnosticSeverity.Information);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
// 检查未闭合的字符串(支持多行文档检查)
|
||||
static checkForUnclosedStrings(document) {
|
||||
const diagnostics = [];
|
||||
const text = document.getText();
|
||||
// 使用状态机来正确解析字符串
|
||||
let inString = false;
|
||||
let stringStartPos = null;
|
||||
let stringChar = '';
|
||||
let escaped = false;
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
const lines = text.split('\n');
|
||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
||||
const line = lines[lineIndex];
|
||||
// 检查是否整行都是注释(以 // 开头)
|
||||
const trimmedLine = line.trim();
|
||||
if (trimmedLine.startsWith('//')) {
|
||||
inLineComment = true;
|
||||
}
|
||||
for (let charIndex = 0; charIndex < line.length; charIndex++) {
|
||||
const char = line[charIndex];
|
||||
const nextChar = charIndex < line.length - 1 ? line[charIndex + 1] : '';
|
||||
// 处理注释
|
||||
if (!inString) {
|
||||
if (!inBlockComment && char === '/' && nextChar === '/') {
|
||||
inLineComment = true;
|
||||
charIndex++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
if (!inLineComment && char === '/' && nextChar === '*') {
|
||||
inBlockComment = true;
|
||||
charIndex++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
if (inBlockComment && char === '*' && nextChar === '/') {
|
||||
inBlockComment = false;
|
||||
charIndex++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 如果在注释中,跳过所有处理
|
||||
if (inLineComment || inBlockComment) {
|
||||
continue;
|
||||
}
|
||||
if (!inString) {
|
||||
// 检查字符串开始
|
||||
if (char === '"' || char === "'") {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
stringStartPos = new vscode.Position(lineIndex, charIndex);
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 在字符串中
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (char === stringChar) {
|
||||
// 字符串结束
|
||||
inString = false;
|
||||
stringStartPos = null;
|
||||
stringChar = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 行结束处理
|
||||
inLineComment = false; // 行注释在行末结束
|
||||
// 只有在非注释行且有未闭合字符串时才报告错误
|
||||
if (inString && stringStartPos && stringStartPos.line === lineIndex && !trimmedLine.startsWith('//')) {
|
||||
// 如果字符串在当前行开始但没有结束,报告错误
|
||||
const range = new vscode.Range(stringStartPos, new vscode.Position(lineIndex, line.length));
|
||||
const diagnostic = new vscode.Diagnostic(range, `字符串未正确闭合:缺少结束${stringChar === '"' ? '双' : '单'}引号`, vscode.DiagnosticSeverity.Error);
|
||||
diagnostics.push(diagnostic);
|
||||
// 重置状态,避免后续行都报错
|
||||
inString = false;
|
||||
stringStartPos = null;
|
||||
}
|
||||
}
|
||||
// 如果文档结束时还有未闭合的字符串,并且不在注释中
|
||||
if (inString && stringStartPos) {
|
||||
const lastLine = lines.length - 1;
|
||||
const lastLineTrimmed = lines[lastLine].trim();
|
||||
if (!lastLineTrimmed.startsWith('//')) {
|
||||
const range = new vscode.Range(stringStartPos, new vscode.Position(lastLine, lines[lastLine].length));
|
||||
const diagnostic = new vscode.Diagnostic(range, `字符串未正确闭合:缺少结束${stringChar === '"' ? '双' : '单'}引号`, vscode.DiagnosticSeverity.Error);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
// 检查未闭合的括号(支持多行文档检查)
|
||||
static checkForUnclosedBrackets(document) {
|
||||
const diagnostics = [];
|
||||
const text = document.getText();
|
||||
// 括号栈,记录每个开括号的位置和类型
|
||||
const bracketStack = [];
|
||||
const bracketPairs = [
|
||||
{ open: '(', close: ')', name: '圆括号' },
|
||||
{ open: '[', close: ']', name: '方括号' },
|
||||
{ open: '{', close: '}', name: '花括号' }
|
||||
];
|
||||
// 状态追踪
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let escaped = false;
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
const lines = text.split('\n');
|
||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
||||
const line = lines[lineIndex];
|
||||
// 检查是否整行都是注释(以 // 开头)
|
||||
const trimmedLine = line.trim();
|
||||
const isCommentLine = trimmedLine.startsWith('//');
|
||||
for (let charIndex = 0; charIndex < line.length; charIndex++) {
|
||||
const char = line[charIndex];
|
||||
const nextChar = charIndex < line.length - 1 ? line[charIndex + 1] : '';
|
||||
// 处理注释
|
||||
if (!inString) {
|
||||
if (!inBlockComment && char === '/' && nextChar === '/') {
|
||||
inLineComment = true;
|
||||
charIndex++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
if (!inLineComment && char === '/' && nextChar === '*') {
|
||||
inBlockComment = true;
|
||||
charIndex++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
if (inBlockComment && char === '*' && nextChar === '/') {
|
||||
inBlockComment = false;
|
||||
charIndex++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 如果在注释中,跳过所有处理
|
||||
if (inLineComment || inBlockComment || isCommentLine) {
|
||||
continue;
|
||||
}
|
||||
// 处理字符串状态
|
||||
if (!inString) {
|
||||
if (char === '"' || char === "'") {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (char === stringChar) {
|
||||
inString = false;
|
||||
stringChar = '';
|
||||
}
|
||||
continue; // 在字符串中,跳过括号检查
|
||||
}
|
||||
// 检查括号(只在非字符串、非注释中)
|
||||
if (!inString && !inLineComment && !inBlockComment && !isCommentLine) {
|
||||
const openBracket = bracketPairs.find(bp => bp.open === char);
|
||||
if (openBracket) {
|
||||
bracketStack.push({
|
||||
char: char,
|
||||
position: new vscode.Position(lineIndex, charIndex),
|
||||
name: openBracket.name,
|
||||
inComment: false
|
||||
});
|
||||
}
|
||||
else {
|
||||
const closeBracket = bracketPairs.find(bp => bp.close === char);
|
||||
if (closeBracket) {
|
||||
if (bracketStack.length === 0) {
|
||||
// 多余的闭合括号
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, charIndex), new vscode.Position(lineIndex, charIndex + 1));
|
||||
const diagnostic = new vscode.Diagnostic(range, `多余的${closeBracket.name}闭合符`, vscode.DiagnosticSeverity.Error);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
else {
|
||||
const lastOpen = bracketStack.pop();
|
||||
const expectedClose = bracketPairs.find(bp => bp.open === lastOpen.char)?.close;
|
||||
if (expectedClose !== char) {
|
||||
// 括号不匹配
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, charIndex), new vscode.Position(lineIndex, charIndex + 1));
|
||||
const diagnostic = new vscode.Diagnostic(range, `括号不匹配:期望 "${expectedClose}" 但找到 "${char}"`, vscode.DiagnosticSeverity.Error);
|
||||
diagnostics.push(diagnostic);
|
||||
// 也标记未匹配的开括号
|
||||
const openRange = new vscode.Range(lastOpen.position, new vscode.Position(lastOpen.position.line, lastOpen.position.character + 1));
|
||||
const openDiagnostic = new vscode.Diagnostic(openRange, `未匹配的${lastOpen.name}`, vscode.DiagnosticSeverity.Error);
|
||||
diagnostics.push(openDiagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 行结束处理
|
||||
inLineComment = false; // 行注释在行末结束
|
||||
}
|
||||
// 检查未闭合的开括号(排除在注释中的括号)
|
||||
bracketStack.forEach(bracket => {
|
||||
if (!bracket.inComment) {
|
||||
const range = new vscode.Range(bracket.position, new vscode.Position(bracket.position.line, bracket.position.character + 1));
|
||||
const diagnostic = new vscode.Diagnostic(range, `未闭合的${bracket.name}`, vscode.DiagnosticSeverity.Error);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
});
|
||||
return diagnostics;
|
||||
}
|
||||
// 检查可能的拼写错误
|
||||
static checkForPossibleTypos(lineText, lineIndex) {
|
||||
const diagnostics = [];
|
||||
// 跳过空行
|
||||
const trimmedLine = lineText.trim();
|
||||
if (trimmedLine.length === 0) {
|
||||
return diagnostics;
|
||||
}
|
||||
// 更完整的Squirrel关键字列表
|
||||
const squirrelKeywords = [
|
||||
'function', 'local', 'if', 'else', 'while', 'for', 'foreach', 'in',
|
||||
'return', 'break', 'continue', 'class', 'extends', 'constructor',
|
||||
'null', 'true', 'false', 'try', 'catch', 'throw', 'const',
|
||||
'enum', 'switch', 'case', 'default', 'delete', 'typeof',
|
||||
'clone', 'resume', 'yield', 'static', 'this', 'base'
|
||||
];
|
||||
// 提取标识符(避免数字、操作符等)
|
||||
const wordPattern = /\b[a-zA-Z_]\w{2,}\b/g;
|
||||
let match;
|
||||
while ((match = wordPattern.exec(lineText)) !== null) {
|
||||
const word = match[0];
|
||||
const wordLower = word.toLowerCase();
|
||||
const startIndex = match.index;
|
||||
// 检查这个单词是否在注释或字符串中
|
||||
if (this.isPositionInCommentOrString(lineText, startIndex)) {
|
||||
continue;
|
||||
}
|
||||
// 跳过已知的关键字(正确拼写)
|
||||
if (squirrelKeywords.includes(wordLower)) {
|
||||
continue;
|
||||
}
|
||||
// 跳过常见的标识符模式(避免误报)
|
||||
if (this.isLikelyValidIdentifier(word)) {
|
||||
continue;
|
||||
}
|
||||
// 检查是否与关键字相似
|
||||
const similarKeyword = squirrelKeywords.find(keyword => {
|
||||
const distance = this.editDistance(wordLower, keyword);
|
||||
// 只对编辑距离为1-2且长度相近的单词提示
|
||||
return distance >= 1 && distance <= 2 &&
|
||||
Math.abs(word.length - keyword.length) <= 2;
|
||||
});
|
||||
if (similarKeyword) {
|
||||
const range = new vscode.Range(new vscode.Position(lineIndex, startIndex), new vscode.Position(lineIndex, startIndex + word.length));
|
||||
const diagnostic = new vscode.Diagnostic(range, `可能的拼写错误:您是否想输入 "${similarKeyword}"?`, vscode.DiagnosticSeverity.Hint);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
// 检查指定位置是否在注释或字符串中
|
||||
static isPositionInCommentOrString(line, position) {
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let escaped = false;
|
||||
let inBlockComment = false;
|
||||
for (let i = 0; i < position && i < line.length; i++) {
|
||||
const char = line[i];
|
||||
const nextChar = i < line.length - 1 ? line[i + 1] : '';
|
||||
if (inBlockComment) {
|
||||
if (char === '*' && nextChar === '/') {
|
||||
inBlockComment = false;
|
||||
i++; // 跳过 '/'
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 检查行注释
|
||||
if (char === '/' && nextChar === '/') {
|
||||
return true; // 位置在行注释中
|
||||
}
|
||||
// 检查块注释开始
|
||||
if (char === '/' && nextChar === '*') {
|
||||
inBlockComment = true;
|
||||
i++; // 跳过 '*'
|
||||
continue;
|
||||
}
|
||||
if (!inString) {
|
||||
if (char === '"' || char === "'") {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (char === stringChar) {
|
||||
inString = false;
|
||||
stringChar = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return inString || inBlockComment;
|
||||
}
|
||||
// 移除字符串和注释内容,避免对其中的内容进行拼写检查
|
||||
static removeStringsAndComments(line) {
|
||||
let result = '';
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let escaped = false;
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const char = line[i];
|
||||
const nextChar = i < line.length - 1 ? line[i + 1] : '';
|
||||
// 如果已经在行注释中,所有后续字符都用空格替换
|
||||
if (inLineComment) {
|
||||
result += ' ';
|
||||
continue;
|
||||
}
|
||||
// 如果已经在块注释中,检查结束标记
|
||||
if (inBlockComment) {
|
||||
if (char === '*' && nextChar === '/') {
|
||||
inBlockComment = false;
|
||||
result += ' '; // 用空格替换 */
|
||||
i++; // 跳过下一个字符
|
||||
}
|
||||
else {
|
||||
result += ' '; // 用空格替换块注释内容
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!inString) {
|
||||
// 检查行注释开始
|
||||
if (char === '/' && nextChar === '/') {
|
||||
inLineComment = true;
|
||||
result += ' '; // 用空格替换 //
|
||||
i++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
// 检查块注释开始
|
||||
if (char === '/' && nextChar === '*') {
|
||||
inBlockComment = true;
|
||||
result += ' '; // 用空格替换 /*
|
||||
i++; // 跳过下一个字符
|
||||
continue;
|
||||
}
|
||||
// 检查字符串开始
|
||||
if (char === '"' || char === "'") {
|
||||
inString = true;
|
||||
stringChar = char;
|
||||
escaped = false;
|
||||
result += ' '; // 用空格替换字符串内容
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 在字符串中处理转义和结束
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
result += ' ';
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
result += ' ';
|
||||
continue;
|
||||
}
|
||||
if (char === stringChar) {
|
||||
inString = false;
|
||||
stringChar = '';
|
||||
result += ' ';
|
||||
continue;
|
||||
}
|
||||
result += ' '; // 用空格替换字符串内容
|
||||
continue;
|
||||
}
|
||||
// 保留非字符串、非注释的字符
|
||||
result += char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// 判断是否是可能的有效标识符(减少误报)
|
||||
static isLikelyValidIdentifier(word) {
|
||||
// 常见的有效标识符模式
|
||||
const validPatterns = [
|
||||
/^[A-Z][A-Z_0-9]+$/, // 常量名(全大写)
|
||||
/^[a-z][a-zA-Z0-9]*$/, // 驼峰命名
|
||||
/^[a-z]+_[a-z0-9_]*$/, // 下划线命名
|
||||
/^[A-Z][a-zA-Z0-9]*$/, // 帕斯卡命名
|
||||
/^\w+\d+$/, // 带数字结尾
|
||||
/^get[A-Z]/, // getter方法
|
||||
/^set[A-Z]/, // setter方法
|
||||
/^is[A-Z]/, // 布尔方法
|
||||
/^has[A-Z]/, // 检查方法
|
||||
];
|
||||
return validPatterns.some(pattern => pattern.test(word));
|
||||
}
|
||||
// 计算两个字符串之间的编辑距离(Levenshtein距离)
|
||||
static editDistance(str1, str2) {
|
||||
const matrix = [];
|
||||
// 初始化矩阵
|
||||
for (let i = 0; i <= str2.length; i++) {
|
||||
matrix[i] = [i];
|
||||
}
|
||||
for (let j = 0; j <= str1.length; j++) {
|
||||
matrix[0][j] = j;
|
||||
}
|
||||
// 填充矩阵
|
||||
for (let i = 1; i <= str2.length; i++) {
|
||||
for (let j = 1; j <= str1.length; j++) {
|
||||
if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
|
||||
matrix[i][j] = matrix[i - 1][j - 1];
|
||||
}
|
||||
else {
|
||||
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // 替换
|
||||
matrix[i][j - 1] + 1, // 插入
|
||||
matrix[i - 1][j] + 1 // 删除
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matrix[str2.length][str1.length];
|
||||
}
|
||||
// 实现CodeActionProvider接口
|
||||
async provideCodeActions(document, range, context, token) {
|
||||
const actions = [];
|
||||
// 为诊断问题提供快速修复
|
||||
for (const diagnostic of context.diagnostics) {
|
||||
// 拼写错误快速修复
|
||||
if (diagnostic.message.includes('可能的拼写错误:您是否想输入')) {
|
||||
const match = diagnostic.message.match(/您是否想输入 "([^"]+)"\?/);
|
||||
if (match) {
|
||||
const correctWord = match[1];
|
||||
const action = new vscode.CodeAction(`将 "${document.getText(diagnostic.range)}" 更正为 "${correctWord}"`, vscode.CodeActionKind.QuickFix);
|
||||
action.edit = new vscode.WorkspaceEdit();
|
||||
action.edit.replace(document.uri, diagnostic.range, correctWord);
|
||||
action.isPreferred = true;
|
||||
actions.push(action);
|
||||
}
|
||||
}
|
||||
// 明显拼写错误快速修复
|
||||
else if (diagnostic.message.includes('拼写错误:应该是')) {
|
||||
const match = diagnostic.message.match(/应该是 "([^"]+)" 而不是 "[^"]+"/);
|
||||
if (match) {
|
||||
const correctWord = match[1];
|
||||
const action = new vscode.CodeAction(`更正拼写为 "${correctWord}"`, vscode.CodeActionKind.QuickFix);
|
||||
action.edit = new vscode.WorkspaceEdit();
|
||||
action.edit.replace(document.uri, diagnostic.range, correctWord);
|
||||
action.isPreferred = true;
|
||||
actions.push(action);
|
||||
}
|
||||
}
|
||||
// 添加分号快速修复
|
||||
else if (diagnostic.message.includes('可能缺少分号')) {
|
||||
const action = new vscode.CodeAction('添加分号', vscode.CodeActionKind.QuickFix);
|
||||
action.edit = new vscode.WorkspaceEdit();
|
||||
const line = document.lineAt(diagnostic.range.start.line);
|
||||
const endPos = new vscode.Position(line.lineNumber, line.text.length);
|
||||
action.edit.insert(document.uri, endPos, ';');
|
||||
actions.push(action);
|
||||
}
|
||||
// 修复赋值操作符错误
|
||||
else if (diagnostic.message.includes('使用了赋值操作符 "=" 而不是比较操作符')) {
|
||||
const action = new vscode.CodeAction('将 "=" 更改为 "=="', vscode.CodeActionKind.QuickFix);
|
||||
action.edit = new vscode.WorkspaceEdit();
|
||||
const text = document.getText(diagnostic.range);
|
||||
const fixedText = text.replace(/([^=])=([^=])/g, '$1==$2');
|
||||
action.edit.replace(document.uri, diagnostic.range, fixedText);
|
||||
actions.push(action);
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
exports.CodeErrorProvider = CodeErrorProvider;
|
||||
//# sourceMappingURL=codeErrorProvider.js.map
|
||||
1
dist/providers/codeErrorProvider.js.map
vendored
Normal file
1
dist/providers/codeErrorProvider.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
288
dist/providers/completionProvider.js
vendored
Normal file
288
dist/providers/completionProvider.js
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DotCompletionProvider = exports.CompletionProvider = exports.CompletionItemType = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const apiParser_1 = require("./apiParser");
|
||||
// 自动完成项类型枚举
|
||||
var CompletionItemType;
|
||||
(function (CompletionItemType) {
|
||||
CompletionItemType["Function"] = "function";
|
||||
CompletionItemType["Variable"] = "variable";
|
||||
CompletionItemType["Keyword"] = "keyword";
|
||||
CompletionItemType["Constant"] = "constant";
|
||||
CompletionItemType["Class"] = "class";
|
||||
CompletionItemType["Property"] = "property";
|
||||
CompletionItemType["Method"] = "method";
|
||||
})(CompletionItemType || (exports.CompletionItemType = CompletionItemType = {}));
|
||||
// 基础关键字列表
|
||||
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'
|
||||
];
|
||||
// 基础自动完成提供者类
|
||||
class CompletionProvider {
|
||||
constructor(cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
this.apiParser = apiParser_1.ApiParser.getInstance();
|
||||
}
|
||||
// 提供自动完成项目列表
|
||||
async provideCompletionItems(document, position, token, context) {
|
||||
const completions = [];
|
||||
// 添加关键字完成项
|
||||
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;
|
||||
}
|
||||
// 获取关键字完成项
|
||||
getKeywordCompletions() {
|
||||
return KEYWORDS.map(keyword => {
|
||||
const item = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||||
item.detail = 'Squirrel 关键字';
|
||||
return item;
|
||||
});
|
||||
}
|
||||
// 获取API函数完成项
|
||||
getApiFunctionCompletions() {
|
||||
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类完成项
|
||||
getApiClassCompletions() {
|
||||
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常量完成项
|
||||
getApiConstantCompletions() {
|
||||
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;
|
||||
});
|
||||
}
|
||||
// 获取常量完成项
|
||||
getConstantCompletions() {
|
||||
return CONSTANTS.map(constant => {
|
||||
const item = new vscode.CompletionItem(constant, vscode.CompletionItemKind.Constant);
|
||||
item.detail = 'Squirrel 常量';
|
||||
return item;
|
||||
});
|
||||
}
|
||||
// 获取跨文件函数完成项
|
||||
async getCrossFileFunctionCompletions() {
|
||||
const completions = [];
|
||||
const allFunctions = this.cacheManager.getAllFunctions();
|
||||
// 按函数名分组,处理重复函数名的情况
|
||||
const functionMap = new Map();
|
||||
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;
|
||||
}
|
||||
// 获取文档中的变量完成项
|
||||
getDocumentVariableCompletions(document, position) {
|
||||
const completions = [];
|
||||
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(); // 使用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;
|
||||
}
|
||||
}
|
||||
exports.CompletionProvider = CompletionProvider;
|
||||
// 点号触发的自动完成提供者类(用于对象属性和方法)
|
||||
class DotCompletionProvider {
|
||||
async provideCompletionItems(document, position, token, context) {
|
||||
// 查找最后一个点号的位置
|
||||
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 = [];
|
||||
// 这里应该根据对象类型提供相应的属性和方法
|
||||
// 由于这是一个简化实现,我们提供一些常见的对象方法
|
||||
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;
|
||||
}
|
||||
// 获取字符串方法完成项
|
||||
getStringMethodCompletions() {
|
||||
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;
|
||||
});
|
||||
}
|
||||
// 获取数组方法完成项
|
||||
getArrayMethodCompletions() {
|
||||
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;
|
||||
});
|
||||
}
|
||||
// 获取表方法完成项
|
||||
getTableMethodCompletions() {
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DotCompletionProvider = DotCompletionProvider;
|
||||
//# sourceMappingURL=completionProvider.js.map
|
||||
1
dist/providers/completionProvider.js.map
vendored
Normal file
1
dist/providers/completionProvider.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
83
dist/providers/definitionProvider.js
vendored
Normal file
83
dist/providers/definitionProvider.js
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefinitionProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
// 定义跳转提供者类
|
||||
class DefinitionProvider {
|
||||
constructor(cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
// 提供符号定义位置
|
||||
async provideDefinition(document, position, token) {
|
||||
// 检查是否在注释中,如果在注释中则不提供定义跳转
|
||||
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 = [];
|
||||
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;
|
||||
}
|
||||
}
|
||||
exports.DefinitionProvider = DefinitionProvider;
|
||||
//# sourceMappingURL=definitionProvider.js.map
|
||||
1
dist/providers/definitionProvider.js.map
vendored
Normal file
1
dist/providers/definitionProvider.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"definitionProvider.js","sourceRoot":"","sources":["../../src/providers/definitionProvider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAGjC,WAAW;AACX,MAAa,kBAAkB;IAG3B,YAAY,YAAkC;QAC1C,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;IAED,WAAW;IACX,KAAK,CAAC,iBAAiB,CACnB,QAA6B,EAC7B,QAAyB,EACzB,KAA+B;QAE/B,0BAA0B;QAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,YAAY,IAAI,CAAC,IAAI,QAAQ,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;YACzD,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,cAAc;QACd,MAAM,KAAK,GAAG,QAAQ,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,OAAO;QACP,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAErC,SAAS;QACT,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,SAAS;gBACT,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC3D,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC7D,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC9C,CAAC;iBAAM,CAAC;gBACJ,kBAAkB;gBAClB,MAAM,SAAS,GAAsB,EAAE,CAAC;gBACxC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;oBACrB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC3D,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;oBAC7D,SAAS,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;gBACvD,CAAC,CAAC,CAAC;gBACH,OAAO,SAAS,CAAC;YACrB,CAAC;QACL,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ;AApDD,gDAoDC"}
|
||||
116
dist/providers/documentFormattingProvider.js
vendored
Normal file
116
dist/providers/documentFormattingProvider.js
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DocumentFormattingProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
// 文档格式化提供者类
|
||||
class DocumentFormattingProvider {
|
||||
// 提供整个文档的代码格式化功能
|
||||
async provideDocumentFormattingEdits(document, options, token) {
|
||||
const edits = [];
|
||||
const lines = [];
|
||||
// 逐行处理文档内容
|
||||
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;
|
||||
}
|
||||
// 格式化文档
|
||||
formatDocument(lines, options) {
|
||||
const formattedLines = [];
|
||||
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));
|
||||
}
|
||||
// 在操作符周围添加适当空格
|
||||
normalizeSpaces(line) {
|
||||
// 在常见的操作符周围添加空格
|
||||
return line
|
||||
.replace(/([^\s])(==|!=|<=|>=|<|>|=|\+|-|\*|\/|%)([^\s])/g, '$1 $2 $3')
|
||||
.replace(/([^\s])(,)([^\s])/g, '$1$2 $3')
|
||||
.replace(/\s+/g, ' ') // 将多个空格替换为单个空格
|
||||
.trim();
|
||||
}
|
||||
// 创建指定级别的缩进
|
||||
createIndent(level, options) {
|
||||
if (options.insertSpaces) {
|
||||
return ' '.repeat(level * options.tabSize);
|
||||
}
|
||||
else {
|
||||
return '\t'.repeat(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.DocumentFormattingProvider = DocumentFormattingProvider;
|
||||
//# sourceMappingURL=documentFormattingProvider.js.map
|
||||
1
dist/providers/documentFormattingProvider.js.map
vendored
Normal file
1
dist/providers/documentFormattingProvider.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"documentFormattingProvider.js","sourceRoot":"","sources":["../../src/providers/documentFormattingProvider.ts"],"names":[],"mappings":"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"}
|
||||
186
dist/providers/hoverProvider.js
vendored
Normal file
186
dist/providers/hoverProvider.js
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.HoverProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const apiParser_1 = require("./apiParser");
|
||||
// 悬停信息提供者类
|
||||
class HoverProvider {
|
||||
constructor(cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
this.apiParser = apiParser_1.ApiParser.getInstance();
|
||||
}
|
||||
// 提供悬停时显示的信息
|
||||
async provideHover(document, position, token) {
|
||||
// 检查是否在注释中,如果在注释中则不提供悬停信息
|
||||
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;
|
||||
}
|
||||
// 获取关键字信息
|
||||
getKeywordInfo(word) {
|
||||
const keywordMap = {
|
||||
'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];
|
||||
}
|
||||
// 获取常量信息
|
||||
getConstantInfo(word) {
|
||||
const constantMap = {
|
||||
'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];
|
||||
}
|
||||
}
|
||||
exports.HoverProvider = HoverProvider;
|
||||
//# sourceMappingURL=hoverProvider.js.map
|
||||
1
dist/providers/hoverProvider.js.map
vendored
Normal file
1
dist/providers/hoverProvider.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
149
dist/providers/onTypeFormattingProvider.js
vendored
Normal file
149
dist/providers/onTypeFormattingProvider.js
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.OnTypeFormattingProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
// 输入时格式化提供者类
|
||||
class OnTypeFormattingProvider {
|
||||
// 在用户输入特定字符时自动格式化代码
|
||||
async provideOnTypeFormattingEdits(document, position, ch, options, token) {
|
||||
const edits = [];
|
||||
// 根据输入的字符执行不同的格式化操作
|
||||
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;
|
||||
}
|
||||
// 格式化当前行
|
||||
formatCurrentLine(document, position, options) {
|
||||
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 [];
|
||||
}
|
||||
// 格式化代码块
|
||||
formatCodeBlock(document, position, options) {
|
||||
const edits = [];
|
||||
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;
|
||||
}
|
||||
// 获取行的缩进级别
|
||||
getIndentLevel(line) {
|
||||
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;
|
||||
}
|
||||
// 创建指定级别的缩进
|
||||
createIndent(level, options) {
|
||||
if (options.insertSpaces) {
|
||||
return ' '.repeat(level);
|
||||
}
|
||||
else {
|
||||
return '\t'.repeat(Math.floor(level / 4)); // 假设一个tab等于4个空格
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.OnTypeFormattingProvider = OnTypeFormattingProvider;
|
||||
//# sourceMappingURL=onTypeFormattingProvider.js.map
|
||||
1
dist/providers/onTypeFormattingProvider.js.map
vendored
Normal file
1
dist/providers/onTypeFormattingProvider.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"onTypeFormattingProvider.js","sourceRoot":"","sources":["../../src/providers/onTypeFormattingProvider.ts"],"names":[],"mappings":"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"}
|
||||
152
dist/providers/signatureHelpProvider.js
vendored
Normal file
152
dist/providers/signatureHelpProvider.js
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SignatureHelpProvider = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const apiParser_1 = require("./apiParser");
|
||||
// 签名帮助提供者类
|
||||
class SignatureHelpProvider {
|
||||
constructor(cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
this.apiParser = apiParser_1.ApiParser.getInstance();
|
||||
}
|
||||
// 提供函数调用时的参数信息和帮助
|
||||
async provideSignatureHelp(document, position, token, context) {
|
||||
// 获取光标前的文本
|
||||
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;
|
||||
}
|
||||
// 查找当前的函数调用
|
||||
findCurrentFunctionCall(text) {
|
||||
// 简化的实现,实际项目中可能需要更复杂的解析
|
||||
// 查找最近的开括号
|
||||
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
|
||||
};
|
||||
}
|
||||
// 创建签名信息
|
||||
createSignatureInformation(func) {
|
||||
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签名信息
|
||||
createApiSignatureInformation(apiFunc) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
exports.SignatureHelpProvider = SignatureHelpProvider;
|
||||
//# sourceMappingURL=signatureHelpProvider.js.map
|
||||
1
dist/providers/signatureHelpProvider.js.map
vendored
Normal file
1
dist/providers/signatureHelpProvider.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"signatureHelpProvider.js","sourceRoot":"","sources":["../../src/providers/signatureHelpProvider.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user