diff --git a/copy-beautify-files.js b/copy-beautify-files.js new file mode 100644 index 0000000..9912ab2 --- /dev/null +++ b/copy-beautify-files.js @@ -0,0 +1,34 @@ +const fs = require('fs-extra'); +const path = require('path'); + +// 复制 js-beautify 相关文件到 dist 目录 +async function copyBeautifyFiles() { + const srcDir = path.join(__dirname); + const destDir = path.join(__dirname, 'dist'); + + // 确保目标目录存在 + await fs.ensureDir(destDir); + + // 复制 js-beautify.js 和包装器 + const filesToCopy = [ + 'js-beautify.js', + 'js-beautify-wrapper.js' + ]; + + for (const file of filesToCopy) { + const srcPath = path.join(srcDir, file); + const destPath = path.join(destDir, file); + + if (await fs.pathExists(srcPath)) { + await fs.copy(srcPath, destPath); + console.log(`已复制 ${file} 到 dist 目录`); + } + } +} + +// 如果直接运行此脚本,则执行复制操作 +if (require.main === module) { + copyBeautifyFiles().catch(console.error); +} + +module.exports = { copyBeautifyFiles }; \ No newline at end of file diff --git a/dist/providers/documentFormattingProvider.js b/dist/providers/documentFormattingProvider.js index b02ec80..5dcb608 100644 --- a/dist/providers/documentFormattingProvider.js +++ b/dist/providers/documentFormattingProvider.js @@ -35,82 +35,39 @@ var __importStar = (this && this.__importStar) || (function () { Object.defineProperty(exports, "__esModule", { value: true }); exports.DocumentFormattingProvider = void 0; const vscode = __importStar(require("vscode")); -// 文档格式化提供者类 +const beautifyHelper_1 = require("../utils/beautifyHelper"); +/** + * 文档格式化提供者类 - 使用 js-beautify.js + */ 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); + try { + // 获取文档全文 + const fullText = document.getText(); + // 检查是否是 Squirrel 代码 + if (!(0, beautifyHelper_1.isSquirrelCode)(fullText)) { + return edits; // 如果不是 Squirrel 代码,不应用格式化 + } + // 使用 js-beautify 格式化代码 + const formattedText = (0, beautifyHelper_1.beautifyCode)(fullText, options); + // 如果格式化后的代码与原代码相同,不需要修改 + if (formattedText === fullText) { + return edits; + } + // 创建编辑操作,替换整个文档 + const fullRange = new vscode.Range(document.positionAt(0), document.positionAt(fullText.length)); + edits.push(vscode.TextEdit.replace(fullRange, formattedText)); + } + catch (error) { + console.error('文档格式化失败:', error); + // 如果格式化失败,返回空编辑数组,保持原代码不变 } - // 格式化文档 - 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 \ No newline at end of file diff --git a/dist/providers/documentFormattingProvider.js.map b/dist/providers/documentFormattingProvider.js.map index a7c0800..dca7335 100644 --- a/dist/providers/documentFormattingProvider.js.map +++ b/dist/providers/documentFormattingProvider.js.map @@ -1 +1 @@ -{"version":3,"file":"documentFormattingProvider.js","sourceRoot":"","sources":["../../src/providers/documentFormattingProvider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAEjC,YAAY;AACZ,MAAa,0BAA0B;IACnC,iBAAiB;IACjB,KAAK,CAAC,8BAA8B,CAChC,QAA6B,EAC7B,OAAiC,EACjC,KAA+B;QAE/B,MAAM,KAAK,GAAsB,EAAE,CAAC;QACpC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,WAAW;QACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAED,QAAQ;QACR,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAE3D,SAAS;QACT,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,KAAK,CAC9B,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EACtB,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CACjD,CAAC;QAEF,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE1E,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,QAAQ;IACA,cAAc,CAAC,KAAe,EAAE,OAAiC;QACrE,MAAM,cAAc,GAAa,EAAE,CAAC;QACpC,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACpB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAEhC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,YAAY;gBACZ,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxB,SAAS;YACb,CAAC;YAED,sBAAsB;YACtB,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5F,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC;YAC/C,CAAC;YAED,UAAU;YACV,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,CAAC;YAC3C,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAEnC,sBAAsB;YACtB,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtF,WAAW,EAAE,CAAC;YAClB,CAAC;YAED,sCAAsC;YACtC,IAAI,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtG,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC5B,MAAM,QAAQ,GAAG,cAAc,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;oBAC3D,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;wBAChC,yBAAyB;wBACzB,cAAc,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,WAAW,CAAC;wBAChF,cAAc,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ;wBAC9B,WAAW,EAAE,CAAC,CAAC,SAAS;oBAC5B,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;QAED,sBAAsB;QACtB,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,eAAe;IACP,eAAe,CAAC,IAAY;QAChC,gBAAgB;QAChB,OAAO,IAAI;aACN,OAAO,CAAC,iDAAiD,EAAE,UAAU,CAAC;aACtE,OAAO,CAAC,oBAAoB,EAAE,SAAS,CAAC;aACxC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,eAAe;aACpC,IAAI,EAAE,CAAC;IAChB,CAAC;IAED,YAAY;IACJ,YAAY,CAAC,KAAa,EAAE,OAAiC;QACjE,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACvB,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACJ,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACL,CAAC;CACJ;AAhGD,gEAgGC"} \ No newline at end of file +{"version":3,"file":"documentFormattingProvider.js","sourceRoot":"","sources":["../../src/providers/documentFormattingProvider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,4DAAuE;AAEvE;;GAEG;AACH,MAAa,0BAA0B;IACnC;;OAEG;IACH,KAAK,CAAC,8BAA8B,CAChC,QAA6B,EAC7B,OAAiC,EACjC,KAA+B;QAE/B,MAAM,KAAK,GAAsB,EAAE,CAAC;QAEpC,IAAI,CAAC;YACD,SAAS;YACT,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;YAEpC,oBAAoB;YACpB,IAAI,CAAC,IAAA,+BAAc,EAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,CAAC,0BAA0B;YAC5C,CAAC;YAED,uBAAuB;YACvB,MAAM,aAAa,GAAG,IAAA,6BAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAEtD,wBAAwB;YACxB,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;gBAC7B,OAAO,KAAK,CAAC;YACjB,CAAC;YAED,gBAAgB;YAChB,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,KAAK,CAC9B,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EACtB,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CACvC,CAAC;YAEF,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;QAElE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YACjC,0BAA0B;QAC9B,CAAC;QAED,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ;AA3CD,gEA2CC"} \ No newline at end of file diff --git a/dist/providers/onTypeFormattingProvider.js b/dist/providers/onTypeFormattingProvider.js index b4a5d05..12042a9 100644 --- a/dist/providers/onTypeFormattingProvider.js +++ b/dist/providers/onTypeFormattingProvider.js @@ -35,115 +35,130 @@ var __importStar = (this && this.__importStar) || (function () { Object.defineProperty(exports, "__esModule", { value: true }); exports.OnTypeFormattingProvider = void 0; const vscode = __importStar(require("vscode")); -// 输入时格式化提供者类 +const beautifyHelper_1 = require("../utils/beautifyHelper"); +/** + * 输入时格式化提供者类 - 使用 js-beautify.js + */ 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; + try { + // 检查是否是 Squirrel 代码 + const textBefore = document.getText(new vscode.Range(new vscode.Position(0, 0), position)); + if (!(0, beautifyHelper_1.isSquirrelCode)(textBefore)) { + return 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; + } + } + catch (error) { + console.error('输入时格式化失败:', error); } return edits; } - // 格式化当前行 + /** + * 使用 js-beautify 格式化当前行 + */ formatCurrentLine(document, position, options) { - const line = document.lineAt(position.line); - const trimmedLine = line.text.trim(); - if (trimmedLine.length === 0) { - return []; + const edits = []; + try { + // 获取当前行 + const line = document.lineAt(position.line); + const lineText = line.text; + // 获取当前行的上下文(向前几行,向后几行) + const startLine = Math.max(0, position.line - 2); + const endLine = Math.min(document.lineCount - 1, position.line + 2); + const contextText = document.getText(new vscode.Range(new vscode.Position(startLine, 0), new vscode.Position(endLine, document.lineAt(endLine).text.length))); + // 使用 js-beautify 格式化代码块 + const formattedContext = (0, beautifyHelper_1.beautifyCode)(contextText, options); + // 比较原始文本和格式化后的文本 + if (formattedContext === contextText) { + return edits; // 没有变化,不需要编辑 + } + // 应用格式化结果 + const originalLines = contextText.split('\n'); + const formattedLines = formattedContext.split('\n'); + for (let i = 0; i < originalLines.length && i < formattedLines.length; i++) { + const original = originalLines[i]; + const formatted = formattedLines[i]; + if (original !== formatted) { + const lineNumber = startLine + i; + const range = new vscode.Range(new vscode.Position(lineNumber, 0), new vscode.Position(lineNumber, document.lineAt(lineNumber).text.length)); + edits.push(vscode.TextEdit.replace(range, formatted)); + } + } } - // 简单的格式化:确保行尾没有多余空格 - 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)]; + catch (error) { + console.error('格式化当前行失败:', error); } - return []; + return edits; } - // 格式化代码块 + /** + * 使用 js-beautify 格式化代码块 + */ 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; + try { + // 查找匹配的左大括号 + 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) { - break; + // 获取代码块的范围 + const endLine = position.line; + const blockText = document.getText(new vscode.Range(new vscode.Position(blockStartLine, 0), new vscode.Position(endLine + 1, 0) // 包含右大括号行 + )); + // 使用 js-beautify 格式化代码块 + const formattedBlock = (0, beautifyHelper_1.beautifyCode)(blockText, options); + if (formattedBlock === blockText) { + return edits; // 没有变化 + } + // 应用格式化结果 + const range = new vscode.Range(new vscode.Position(blockStartLine, 0), new vscode.Position(endLine + 1, 0)); + edits.push(vscode.TextEdit.replace(range, formattedBlock)); } } - 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)); + catch (error) { + console.error('格式化代码块失败:', error); } 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 \ No newline at end of file diff --git a/dist/providers/onTypeFormattingProvider.js.map b/dist/providers/onTypeFormattingProvider.js.map index 6d42135..47b9e33 100644 --- a/dist/providers/onTypeFormattingProvider.js.map +++ b/dist/providers/onTypeFormattingProvider.js.map @@ -1 +1 @@ -{"version":3,"file":"onTypeFormattingProvider.js","sourceRoot":"","sources":["../../src/providers/onTypeFormattingProvider.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"onTypeFormattingProvider.js","sourceRoot":"","sources":["../../src/providers/onTypeFormattingProvider.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/utils/beautifyHelper.js b/dist/utils/beautifyHelper.js new file mode 100644 index 0000000..7e8537a --- /dev/null +++ b/dist/utils/beautifyHelper.js @@ -0,0 +1,201 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getSquirrelBeautifyOptions = getSquirrelBeautifyOptions; +exports.beautifyCode = beautifyCode; +exports.isSquirrelCode = isSquirrelCode; +// 由于 js-beautify.js 文件体积较大且复杂,我们将创建专门的存根函数 +// 将使用原生的 js-beautify 功能 +let _jsBeauifyInstance = null; +/** + * 动态创建 js_beautify 函数 + */ +function createJsBeautify() { + if (_jsBeauifyInstance) { + return _jsBeauifyInstance; + } + try { + // 加载原始的 js-beautify.js 文件 + const beautifyModule = loadJsBeautify(); + _jsBeauifyInstance = beautifyModule; + return _jsBeauifyInstance; + } + catch (error) { + console.error('加载 js-beautify 失败,使用后备实现:', error); + // 调用后备实现 + return createBasicJsBeautify(); + } +} +/** + * 加载 js-beautify 模块 + */ +function loadJsBeautify() { + // 根据环境动态加载 + try { + // 尝试 require 原始的 js-beautify.js 文件 + // 注意:TypeScript 会从 ./dist 目录运行,所以需要从正确的路径加载 + const beautifyPath = __dirname + '/../../js-beautify.js'; + const Module = require('module'); + const vm = require('vm'); + const fs = require('fs'); + // 读取文件内容 + const code = fs.readFileSync(beautifyPath, 'utf8'); + // 设置全局环境 + const sandbox = { + window: {}, + global: {}, + console: console, + String: String, + Array: Array, + Object: Object, + Math: Math + }; + // 在沙箱中执行代码 + const script = new vm.Script(code); + const context = vm.createContext(sandbox); + script.runInContext(context); + // 获取 js_beautify 函数 + return context.window.js_beautify || context.js_beautify || sandbox.window.js_beautify; + } + catch (loadError) { + console.error('无法加载原始 js-beautify, 错误:', loadError); + throw loadError; + } +} +/** + * 基础的 js-beautify 实现(后备方案) + */ +function createBasicJsBeautify() { + return function (code, options = {}) { + // 默认选项 + const defaultOptions = { + indent_size: 4, + indent_char: ' ', + preserve_newlines: true, + brace_style: 'collapse', + space_before_conditional: true, + space_after_anon_function: false, + wrap_line_length: 120, + end_with_newline: false + }; + // 合并选项 + const opts = { ...defaultOptions, ...options }; + // 简化的代码美化实现 + let formatted = code; + // 清理缩进 + formatted = formatted.replace(/[\r\n]+/g, '\n'); + const lines = formatted.split('\n'); + const result = []; + let indentLevel = 0; + const indentStr = opts.indent_char.repeat(opts.indent_size); + for (const line of lines) { + const trimmed = line.trim(); + // 跳过空行 + if (trimmed.length === 0) { + result.push(''); + continue; + } + // 处理大括号 + if (trimmed.startsWith('}') || trimmed.startsWith(')') || trimmed.startsWith(']')) { + indentLevel = Math.max(0, indentLevel - 1); + } + // 添加缩进 + const indentedLine = indentStr.repeat(indentLevel) + trimmed; + result.push(indentedLine); + // 增加缩进 + if (trimmed.endsWith('{') || trimmed.endsWith('(') || trimmed.endsWith('[')) { + indentLevel++; + } + // 特殊处理 else、catch 等关键字 + if (trimmed.startsWith('else') || trimmed.startsWith('catch') || trimmed.startsWith('elif')) { + // 检查前面一行是否以 } 结尾 + if (result.length > 1) { + const prevLine = result[result.length - 2]; + const prevTrimmed = prevLine.trim(); + if (prevTrimmed.endsWith('}')) { + // 移动到前一行的必要部分 + result[result.length - 2] = indentStr.repeat(indentLevel - 1) + + prevTrimmed + ' ' + trimmed; + result.pop(); // 移除当前行 + indentLevel--; // 修正缩进级别 + } + } + } + } + let finalResult = result.join('\n'); + // 处理空格 - 在操作符周围添加空格 + finalResult = finalResult + .replace(/([a-zA-Z0-9_$])([=!<>+\-*/%])([a-zA-Z0-9_$])/g, '$1 $2 $3') + .replace(/([a-zA-Z0-9_$])(,)([a-zA-Z0-9_$])/g, '$1$2 $3') + .replace(/\s+/g, ' ') // 将多个空格替换为单个空格 + .trim(); + return finalResult; + }; +} +// 初始化 js_beautify +const jsBeautify = { + js_beautify: createJsBeautify() +}; +/** + * Squirrel 语言的 js-beautify 选项配置 + */ +function getSquirrelBeautifyOptions(options) { + return { + indent_size: options.tabSize, + indent_char: options.insertSpaces ? ' ' : '\t', + preserve_newlines: true, + max_preserve_newlines: 2, + jslint_happy: false, + brace_style: "collapse", + space_before_conditional: true, + space_after_anon_function: false, + unescape_strings: false, + wrap_line_length: 120, + end_with_newline: false + }; +} +/** + * 使用 js-beautify 格式化代码 + */ +function beautifyCode(code, options) { + if (!jsBeautify || !jsBeautify.js_beautify) { + console.warn('js-beautify 不可用,返回原始代码'); + return code; + } + try { + const beautifyOptions = getSquirrelBeautifyOptions(options); + // 检查代码长度,处理大块代码 + const maxChunkSize = 50000; // 50KB 每块 + if (code.length > maxChunkSize) { + // 分割代码并分块处理 + const chunks = code.split('\n'); + const resultChunks = []; + for (const chunk of chunks) { + const formattedChunk = jsBeautify.js_beautify(chunk, beautifyOptions); + resultChunks.push(formattedChunk); + } + return resultChunks.join('\n'); + } + // 直接格式化小代码块 + return jsBeautify.js_beautify(code, beautifyOptions); + } + catch (error) { + console.error('js-beautify 格式化失败:', error); + // 如果格式化失败,返回原始代码 + return code; + } +} +/** + * 检测是否为 Squirrel 代码(用于确定是否应用格式化) + */ +function isSquirrelCode(code) { + // 简单的启发式检测 - Squirrel 特有关键字 + const squirrelKeywords = [ + 'function', 'local', 'if', 'else', 'while', 'for', 'foreach', + 'class', 'constructor', 'extends', 'instanceof', 'in', + 'try', 'catch', 'throw', 'clone', 'delegate', 'resume', 'yield', + 'typeof', 'member', 'rawcall', 'call', 'setdebughook' + ]; + // 提高检测准确性 - 需要至少匹配到1个关键字 + return squirrelKeywords.some(keyword => new RegExp(`\\b${keyword}\\b`).test(code)); +} +//# sourceMappingURL=beautifyHelper.js.map \ No newline at end of file diff --git a/dist/utils/beautifyHelper.js.map b/dist/utils/beautifyHelper.js.map new file mode 100644 index 0000000..83c0bc5 --- /dev/null +++ b/dist/utils/beautifyHelper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"beautifyHelper.js","sourceRoot":"","sources":["../../src/utils/beautifyHelper.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/js-beautify-wrapper-fixed.js b/js-beautify-wrapper-fixed.js new file mode 100644 index 0000000..3abaa68 --- /dev/null +++ b/js-beautify-wrapper-fixed.js @@ -0,0 +1,44 @@ +const fs = require('fs'); +const path = require('path'); + +// 因为 js-beautify.js 直接在全局作用域定义 js_beautify, +// 我们可以直接使用 require 来引入并获取函数 + +// 特别注意:js-beautify.js 假设需要 window 或 global 对象 +// Node.js 环境下,我们需要将 js_beautify 挂载到全局 + +let js_beautify; + +// 读取并执行 js-beautify.js +const beautifyPath = path.join(__dirname, 'js-beautify.js'); +const beautifyCode = fs.readFileSync(beautifyPath, 'utf-8'); + +// 创建一个安全的沙箱环境 +global.window = global; +global.module = undefined; +global.exports = undefined; + +// 执行代码 +try { + eval(beautifyCode); + js_beautify = global.js_beautify; +} catch (error) { + console.error('加载 js-beautify.js 失败:', error); + throw error; +} + +// 清理全局变量 +delete global.window; + +// 如果 js_beautify 没有挂载到 global,尝试从 globalThis 获取 +if (!js_beautify && globalThis.js_beautify) { + js_beautify = globalThis.js_beautify; +} + +if (!js_beautify) { + throw new Error('无法从 js-beautify.js 中找到 js_beautify 函数'); +} + +module.exports = { + js_beautify: js_beautify +}; \ No newline at end of file diff --git a/js-beautify-wrapper.js b/js-beautify-wrapper.js new file mode 100644 index 0000000..7ffc03c --- /dev/null +++ b/js-beautify-wrapper.js @@ -0,0 +1,15 @@ +// js-beautify.js 的 CommonJS 包装器 +const fs = require('fs'); +const path = require('path'); + +// 读取原始 js-beautify.js 文件内容 +const beautifyPath = path.join(__dirname, 'js-beautify.js'); +const beautifyContent = fs.readFileSync(beautifyPath, 'utf-8'); + +// 执行文件内容,确保 js_beautify 函数可用 +eval(beautifyContent); + +// 模块导出 js_beautify 函数 +module.exports = { + js_beautify: global.js_beautify || js_beautify +}; \ No newline at end of file diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index db8c519..6cddfd5 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -1,6 +1,6 @@ { "name": "squirrel-nut-explorer", - "version": "1.0.1", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package-lock.json b/package-lock.json index 4558a9d..8e461a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "squirrel-nut-explorer", - "version": "1.0.1", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "squirrel-nut-explorer", - "version": "1.0.1", + "version": "1.0.0", "devDependencies": { "@types/node": "^20.19.11", "@types/vscode": "^1.99.0", diff --git a/squirrel-nut-explorer-1.0.0.vsix b/squirrel-nut-explorer-1.0.0.vsix new file mode 100644 index 0000000..e2846a1 Binary files /dev/null and b/squirrel-nut-explorer-1.0.0.vsix differ diff --git a/src/providers/documentFormattingProvider.ts b/src/providers/documentFormattingProvider.ts index bd7b9a2..ca13dc7 100644 --- a/src/providers/documentFormattingProvider.ts +++ b/src/providers/documentFormattingProvider.ts @@ -1,100 +1,50 @@ import * as vscode from 'vscode'; +import { beautifyCode, isSquirrelCode } from '../utils/beautifyHelper'; -// 文档格式化提供者类 +/** + * 文档格式化提供者类 - 使用 js-beautify.js + */ export class DocumentFormattingProvider implements vscode.DocumentFormattingEditProvider { - // 提供整个文档的代码格式化功能 + /** + * 提供整个文档的代码格式化功能 + */ async provideDocumentFormattingEdits( document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken ): Promise { const edits: vscode.TextEdit[] = []; - const lines: string[] = []; - // 逐行处理文档内容 - for (let i = 0; i < document.lineCount; i++) { - const line = document.lineAt(i); - lines.push(line.text); + try { + // 获取文档全文 + const fullText = document.getText(); + + // 检查是否是 Squirrel 代码 + if (!isSquirrelCode(fullText)) { + return edits; // 如果不是 Squirrel 代码,不应用格式化 + } + + // 使用 js-beautify 格式化代码 + const formattedText = beautifyCode(fullText, options); + + // 如果格式化后的代码与原代码相同,不需要修改 + if (formattedText === fullText) { + return edits; + } + + // 创建编辑操作,替换整个文档 + const fullRange = new vscode.Range( + document.positionAt(0), + document.positionAt(fullText.length) + ); + + edits.push(vscode.TextEdit.replace(fullRange, formattedText)); + + } catch (error) { + console.error('文档格式化失败:', error); + // 如果格式化失败,返回空编辑数组,保持原代码不变 } - // 格式化文档 - const formattedLines = this.formatDocument(lines, options); - - // 创建编辑操作 - const fullRange = new vscode.Range( - document.positionAt(0), - document.positionAt(document.getText().length) - ); - - edits.push(vscode.TextEdit.replace(fullRange, formattedLines.join('\n'))); - return edits; } - - // 格式化文档 - private formatDocument(lines: string[], options: vscode.FormattingOptions): string[] { - const formattedLines: string[] = []; - let indentLevel = 0; - - for (let i = 0; i < lines.length; i++) { - let line = lines[i]; - const trimmedLine = line.trim(); - - if (trimmedLine.length === 0) { - // 空行保持原样或删除 - formattedLines.push(''); - continue; - } - - // 检查是否需要减少缩进(遇到右大括号等) - if (trimmedLine.startsWith('}') || trimmedLine.startsWith(')') || trimmedLine.startsWith(']')) { - indentLevel = Math.max(0, indentLevel - 1); - } - - // 添加适当的缩进 - const indent = this.createIndent(indentLevel, options); - const formattedLine = indent + trimmedLine; - formattedLines.push(formattedLine); - - // 检查是否需要增加缩进(遇到左大括号等) - if (trimmedLine.endsWith('{') || trimmedLine.endsWith('(') || trimmedLine.endsWith('[')) { - indentLevel++; - } - - // 特殊处理else、catch等关键字,它们应该与前面的右大括号在同一行 - if (trimmedLine.startsWith('else') || trimmedLine.startsWith('catch') || trimmedLine.startsWith('elif')) { - if (formattedLines.length > 1) { - const prevLine = formattedLines[formattedLines.length - 2]; - if (prevLine.trim().endsWith('}')) { - // 将else等关键字与前面的右大括号放在同一行 - formattedLines[formattedLines.length - 2] = prevLine.trim() + ' ' + trimmedLine; - formattedLines.pop(); // 移除当前行 - indentLevel--; // 修正缩进级别 - } - } - } - } - - // 规范化空格(在操作符周围添加适当空格) - return formattedLines.map(line => this.normalizeSpaces(line)); - } - - // 在操作符周围添加适当空格 - private normalizeSpaces(line: string): string { - // 在常见的操作符周围添加空格 - return line - .replace(/([^\s])(==|!=|<=|>=|<|>|=|\+|-|\*|\/|%)([^\s])/g, '$1 $2 $3') - .replace(/([^\s])(,)([^\s])/g, '$1$2 $3') - .replace(/\s+/g, ' ') // 将多个空格替换为单个空格 - .trim(); - } - - // 创建指定级别的缩进 - private createIndent(level: number, options: vscode.FormattingOptions): string { - if (options.insertSpaces) { - return ' '.repeat(level * options.tabSize); - } else { - return '\t'.repeat(level); - } - } } \ No newline at end of file diff --git a/src/providers/onTypeFormattingProvider.ts b/src/providers/onTypeFormattingProvider.ts index ae61296..d95a4e7 100644 --- a/src/providers/onTypeFormattingProvider.ts +++ b/src/providers/onTypeFormattingProvider.ts @@ -1,8 +1,13 @@ import * as vscode from 'vscode'; +import { beautifyCode, isSquirrelCode } from '../utils/beautifyHelper'; -// 输入时格式化提供者类 +/** + * 输入时格式化提供者类 - 使用 js-beautify.js + */ export class OnTypeFormattingProvider implements vscode.OnTypeFormattingEditProvider { - // 在用户输入特定字符时自动格式化代码 + /** + * 在用户输入特定字符时自动格式化代码 + */ async provideOnTypeFormattingEdits( document: vscode.TextDocument, position: vscode.Position, @@ -12,138 +17,162 @@ export class OnTypeFormattingProvider implements vscode.OnTypeFormattingEditProv ): Promise { const edits: vscode.TextEdit[] = []; - // 根据输入的字符执行不同的格式化操作 - switch (ch) { - case ')': - // 输入右括号时格式化当前行 - edits.push(...this.formatCurrentLine(document, position, options)); - break; - case ';': - // 输入分号时格式化当前行 - edits.push(...this.formatCurrentLine(document, position, options)); - break; - case '}': - // 输入右大括号时格式化整个代码块 - edits.push(...this.formatCodeBlock(document, position, options)); - break; - default: - break; + try { + // 检查是否是 Squirrel 代码 + const textBefore = document.getText(new vscode.Range(new vscode.Position(0, 0), position)); + if (!isSquirrelCode(textBefore)) { + return 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; + } + + } catch (error) { + console.error('输入时格式化失败:', error); } return edits; } - // 格式化当前行 + /** + * 使用 js-beautify 格式化当前行 + */ private formatCurrentLine( document: vscode.TextDocument, position: vscode.Position, options: vscode.FormattingOptions ): vscode.TextEdit[] { - const line = document.lineAt(position.line); - const trimmedLine = line.text.trim(); + const edits: vscode.TextEdit[] = []; - if (trimmedLine.length === 0) { - return []; - } + try { + // 获取当前行 + const line = document.lineAt(position.line); + const lineText = line.text; - // 简单的格式化:确保行尾没有多余空格 - 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) + // 获取当前行的上下文(向前几行,向后几行) + const startLine = Math.max(0, position.line - 2); + const endLine = Math.min(document.lineCount - 1, position.line + 2); + + const contextText = document.getText( + new vscode.Range( + new vscode.Position(startLine, 0), + new vscode.Position(endLine, document.lineAt(endLine).text.length) + ) ); - return [vscode.TextEdit.replace(range, trimmedLine)]; + + // 使用 js-beautify 格式化代码块 + const formattedContext = beautifyCode(contextText, options); + + // 比较原始文本和格式化后的文本 + if (formattedContext === contextText) { + return edits; // 没有变化,不需要编辑 + } + + // 应用格式化结果 + const originalLines = contextText.split('\n'); + const formattedLines = formattedContext.split('\n'); + + for (let i = 0; i < originalLines.length && i < formattedLines.length; i++) { + const original = originalLines[i]; + const formatted = formattedLines[i]; + + if (original !== formatted) { + const lineNumber = startLine + i; + const range = new vscode.Range( + new vscode.Position(lineNumber, 0), + new vscode.Position(lineNumber, document.lineAt(lineNumber).text.length) + ); + edits.push(vscode.TextEdit.replace(range, formatted)); + } + } + + } catch (error) { + console.error('格式化当前行失败:', error); } - return []; + return edits; } - // 格式化代码块 + /** + * 使用 js-beautify 格式化代码块 + */ private formatCodeBlock( document: vscode.TextDocument, position: vscode.Position, options: vscode.FormattingOptions ): vscode.TextEdit[] { const edits: vscode.TextEdit[] = []; - const line = document.lineAt(position.line); - // 查找匹配的左大括号 - let braceCount = 1; - let blockStartLine = -1; + try { + // 查找匹配的左大括号 + 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 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; + 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) { - break; - } - } + // 获取代码块的范围 + const endLine = position.line; + const blockText = document.getText( + new vscode.Range( + new vscode.Position(blockStartLine, 0), + new vscode.Position(endLine + 1, 0) // 包含右大括号行 + ) + ); - if (blockStartLine !== -1) { - // 调整代码块的缩进 - const startLine = document.lineAt(blockStartLine); - const startIndent = this.getIndentLevel(startLine.text); + // 使用 js-beautify 格式化代码块 + const formattedBlock = beautifyCode(blockText, options); - 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)); + if (formattedBlock === blockText) { + return edits; // 没有变化 } + + // 应用格式化结果 + const range = new vscode.Range( + new vscode.Position(blockStartLine, 0), + new vscode.Position(endLine + 1, 0) + ); + edits.push(vscode.TextEdit.replace(range, formattedBlock)); } - // 调整右大括号的缩进 - 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)); + } catch (error) { + console.error('格式化代码块失败:', error); } return edits; } - - // 获取行的缩进级别 - private getIndentLevel(line: string): number { - let indent = 0; - for (let i = 0; i < line.length; i++) { - if (line[i] === ' ') { - indent++; - } else if (line[i] === '\t') { - indent += 4; // 假设一个tab等于4个空格 - } else { - break; - } - } - return indent; - } - - // 创建指定级别的缩进 - private createIndent(level: number, options: vscode.FormattingOptions): string { - if (options.insertSpaces) { - return ' '.repeat(level); - } else { - return '\t'.repeat(Math.floor(level / 4)); // 假设一个tab等于4个空格 - } - } } \ No newline at end of file diff --git a/src/utils/beautifyHelper.ts b/src/utils/beautifyHelper.ts new file mode 100644 index 0000000..f4df853 --- /dev/null +++ b/src/utils/beautifyHelper.ts @@ -0,0 +1,228 @@ +import * as vscode from 'vscode'; + +// 由于 js-beautify.js 文件体积较大且复杂,我们将创建专门的存根函数 +// 将使用原生的 js-beautify 功能 + +let _jsBeauifyInstance: any = null; + +/** + * 动态创建 js_beautify 函数 + */ +function createJsBeautify(): any { + if (_jsBeauifyInstance) { + return _jsBeauifyInstance; + } + + try { + // 加载原始的 js-beautify.js 文件 + const beautifyModule = loadJsBeautify(); + _jsBeauifyInstance = beautifyModule; + return _jsBeauifyInstance; + } catch (error) { + console.error('加载 js-beautify 失败,使用后备实现:', error); + // 调用后备实现 + return createBasicJsBeautify(); + } +} + +/** + * 加载 js-beautify 模块 + */ +function loadJsBeautify(): any { + // 根据环境动态加载 + try { + // 尝试 require 原始的 js-beautify.js 文件 + // 注意:TypeScript 会从 ./dist 目录运行,所以需要从正确的路径加载 + const beautifyPath = __dirname + '/../../js-beautify.js'; + const Module = require('module'); + const vm = require('vm'); + const fs = require('fs'); + + // 读取文件内容 + const code = fs.readFileSync(beautifyPath, 'utf8'); + + // 设置全局环境 + const sandbox: any = { + window: {}, + global: {}, + console: console, + String: String, + Array: Array, + Object: Object, + Math: Math + }; + + // 在沙箱中执行代码 + const script = new vm.Script(code); + const context = vm.createContext(sandbox); + script.runInContext(context); + + // 获取 js_beautify 函数 + return context.window.js_beautify || context.js_beautify || sandbox.window.js_beautify; + } catch (loadError) { + console.error('无法加载原始 js-beautify, 错误:', loadError); + throw loadError; + } +} + +/** + * 基础的 js-beautify 实现(后备方案) + */ +function createBasicJsBeautify(): any { + return function(code: string, options: any = {}): string { + // 默认选项 + const defaultOptions = { + indent_size: 4, + indent_char: ' ', + preserve_newlines: true, + brace_style: 'collapse', + space_before_conditional: true, + space_after_anon_function: false, + wrap_line_length: 120, + end_with_newline: false + }; + + // 合并选项 + const opts = { ...defaultOptions, ...options }; + + // 简化的代码美化实现 + let formatted = code; + + // 清理缩进 + formatted = formatted.replace(/[\r\n]+/g, '\n'); + + const lines = formatted.split('\n'); + const result: string[] = []; + let indentLevel = 0; + const indentStr = opts.indent_char.repeat(opts.indent_size); + + for (const line of lines) { + const trimmed = line.trim(); + + // 跳过空行 + if (trimmed.length === 0) { + result.push(''); + continue; + } + + // 处理大括号 + if (trimmed.startsWith('}') || trimmed.startsWith(')') || trimmed.startsWith(']')) { + indentLevel = Math.max(0, indentLevel - 1); + } + + // 添加缩进 + const indentedLine = indentStr.repeat(indentLevel) + trimmed; + result.push(indentedLine); + + // 增加缩进 + if (trimmed.endsWith('{') || trimmed.endsWith('(') || trimmed.endsWith('[')) { + indentLevel++; + } + + // 特殊处理 else、catch 等关键字 + if (trimmed.startsWith('else') || trimmed.startsWith('catch') || trimmed.startsWith('elif')) { + // 检查前面一行是否以 } 结尾 + if (result.length > 1) { + const prevLine = result[result.length - 2]; + const prevTrimmed = prevLine.trim(); + if (prevTrimmed.endsWith('}')) { + // 移动到前一行的必要部分 + result[result.length - 2] = indentStr.repeat(indentLevel - 1) + + prevTrimmed + ' ' + trimmed; + result.pop(); // 移除当前行 + indentLevel--; // 修正缩进级别 + } + } + } + } + + let finalResult = result.join('\n'); + + // 处理空格 - 在操作符周围添加空格 + finalResult = finalResult + .replace(/([a-zA-Z0-9_$])([=!<>+\-*/%])([a-zA-Z0-9_$])/g, '$1 $2 $3') + .replace(/([a-zA-Z0-9_$])(,)([a-zA-Z0-9_$])/g, '$1$2 $3') + .replace(/\s+/g, ' ') // 将多个空格替换为单个空格 + .trim(); + + return finalResult; + }; +} + +// 初始化 js_beautify +const jsBeautify = { + js_beautify: createJsBeautify() +}; + +/** + * Squirrel 语言的 js-beautify 选项配置 + */ +export function getSquirrelBeautifyOptions(options: vscode.FormattingOptions): any { + return { + indent_size: options.tabSize, + indent_char: options.insertSpaces ? ' ' : '\t', + preserve_newlines: true, + max_preserve_newlines: 2, + jslint_happy: false, + brace_style: "collapse", + space_before_conditional: true, + space_after_anon_function: false, + unescape_strings: false, + wrap_line_length: 120, + end_with_newline: false + }; +} + +/** + * 使用 js-beautify 格式化代码 + */ +export function beautifyCode(code: string, options: vscode.FormattingOptions): string { + if (!jsBeautify || !jsBeautify.js_beautify) { + console.warn('js-beautify 不可用,返回原始代码'); + return code; + } + + try { + const beautifyOptions = getSquirrelBeautifyOptions(options); + + // 检查代码长度,处理大块代码 + const maxChunkSize = 50000; // 50KB 每块 + if (code.length > maxChunkSize) { + // 分割代码并分块处理 + const chunks = code.split('\n'); + const resultChunks: string[] = []; + + for (const chunk of chunks) { + const formattedChunk = jsBeautify.js_beautify(chunk, beautifyOptions); + resultChunks.push(formattedChunk); + } + + return resultChunks.join('\n'); + } + + // 直接格式化小代码块 + return jsBeautify.js_beautify(code, beautifyOptions); + } catch (error) { + console.error('js-beautify 格式化失败:', error); + // 如果格式化失败,返回原始代码 + return code; + } +} + +/** + * 检测是否为 Squirrel 代码(用于确定是否应用格式化) + */ +export function isSquirrelCode(code: string): boolean { + // 简单的启发式检测 - Squirrel 特有关键字 + const squirrelKeywords = [ + 'function', 'local', 'if', 'else', 'while', 'for', 'foreach', + 'class', 'constructor', 'extends', 'instanceof', 'in', + 'try', 'catch', 'throw', 'clone', 'delegate', 'resume', 'yield', + 'typeof', 'member', 'rawcall', 'call', 'setdebughook' + ]; + + // 提高检测准确性 - 需要至少匹配到1个关键字 + return squirrelKeywords.some(keyword => + new RegExp(`\\b${keyword}\\b`).test(code) + ); +} \ No newline at end of file