Files
squirrelVsis/dist/providers/codeErrorProvider.js
2025-09-17 10:54:25 +08:00

906 lines
42 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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