diff --git a/.editorconfig b/.editorconfig index ec317dc..3907f89 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,4 +7,7 @@ indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true -max_line_length = 100 \ No newline at end of file +max_line_length = 120 + +[sampleWorkspace/**] +trim_trailing_whitespace = false diff --git a/.vscode/launch.json b/.vscode/launch.json index 59cd810..a9e3779 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,8 @@ "runtimeExecutable": "${execPath}", "args": ["--disable-extensions", "--extensionDevelopmentPath=${workspaceFolder}"], "outFiles": ["${workspaceFolder}/out/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" + "preLaunchTask": "${defaultBuildTask}", + "sourceMaps": true }, { "name": "Test: e2e", @@ -22,7 +23,8 @@ "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json" }, "outFiles": ["${workspaceFolder}/out/**/*.js"], - "preLaunchTask": "npm: test-compile" + "preLaunchTask": "npm: test-compile", + "sourceMaps": true }, { "name": "Test: e2e use mocha", @@ -40,7 +42,8 @@ "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json" }, "outFiles": ["${workspaceFolder}/out/test/**/*.js"], - "preLaunchTask": "npm: test-compile" + "preLaunchTask": "npm: test-compile", + "sourceMaps": true }, { "name": "Debug Current Unit Test File", diff --git a/CHANGELOG.md b/CHANGELOG.md index 53a92a2..9d6a8a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to the "turbo-file-header" extension will be documented in t ## 新增功能 🌱 -- feat: 增加 ts 文件函数注释功能 +- feat: 增加 js/ts 文件函数注释功能 # [0.1.1] diff --git a/eslint.config.mjs b/eslint.config.mjs index e40b917..7f814af 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -23,6 +23,7 @@ export default tseslint.config( '@typescript-eslint/no-explicit-any': 'warn', 'simple-import-sort/imports': 'warn', 'simple-import-sort/exports': 'warn', + 'prettier/prettier': 'warn' }, }, ); diff --git a/sampleWorkspace/file-header/no-fileheader.js b/sampleWorkspace/file-header/no-fileheader.js new file mode 100644 index 0000000..dae7296 --- /dev/null +++ b/sampleWorkspace/file-header/no-fileheader.js @@ -0,0 +1,2 @@ +const testString = 'this is a string'; +console.log('🚀 ~ file: to-test-command-addFileheader.ts:2 ~ testString:', testString); diff --git a/sampleWorkspace/file-header/no-fileheader.result.js b/sampleWorkspace/file-header/no-fileheader.result.js new file mode 100644 index 0000000..6174962 --- /dev/null +++ b/sampleWorkspace/file-header/no-fileheader.result.js @@ -0,0 +1,11 @@ +/* + * @file sampleWorkspace/file-header/no-fileheader.ts + * @description test file header + * @author ygqygq2 + * @createTime 2024-04-11 16:37:11 + * @lastModified 2024-04-11 16:37:59 + * Copyright ©ygqygq2 All rights reserved +*/ + +const testString = 'this is a string'; +console.log('🚀 ~ file: to-test-command-addFileheader.ts:2 ~ testString:', testString); diff --git a/sampleWorkspace/function-comment/variable-arrow-function-with-params-type-update.result.ts b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type-update.result.ts new file mode 100644 index 0000000..6e7f1f7 --- /dev/null +++ b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type-update.result.ts @@ -0,0 +1,10 @@ +/** + * @description test + * @return default {string} + * @param a {string} + * @param b {number} + */ +const func2 = (a: string, b: number): string => { + console.log(a + b); + return a + b; +}; diff --git a/sampleWorkspace/function-comment/variable-arrow-function-with-params-type-update.ts b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type-update.ts new file mode 100644 index 0000000..174d0a6 --- /dev/null +++ b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type-update.ts @@ -0,0 +1,10 @@ +/** + * @description test + * @return default {string} + * @param a {string} + * @param b {string} + */ +const func2 = (a: string, b: number): string => { + console.log(a + b); + return a + b; +}; diff --git a/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.result.ts b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.result.ts index f4aa51e..2231e09 100644 --- a/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.result.ts +++ b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.result.ts @@ -4,7 +4,7 @@ * @param a {string} * @param b {string} */ -const func = (a: string, b: string): string => { +const func1 = (a: string, b: string): string => { console.log(a + b); return a + b; }; diff --git a/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.ts b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.ts index 8cda452..50f1b7e 100644 --- a/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.ts +++ b/sampleWorkspace/function-comment/variable-arrow-function-with-params-type.ts @@ -1,4 +1,4 @@ -const func = (a: string, b: string): string => { +const func1 = (a: string, b: string): string => { console.log(a + b); return a + b; }; diff --git a/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.js b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.js new file mode 100644 index 0000000..59bfc04 --- /dev/null +++ b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.js @@ -0,0 +1,10 @@ +/** + * @description + * @return default {void} + * @param a {any} + * @param b {any} + */ +const func = (a, b) => { + console.log(a + b); + return a + b; +}; diff --git a/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.result.js b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.result.js new file mode 100644 index 0000000..59bfc04 --- /dev/null +++ b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.result.js @@ -0,0 +1,10 @@ +/** + * @description + * @return default {void} + * @param a {any} + * @param b {any} + */ +const func = (a, b) => { + console.log(a + b); + return a + b; +}; diff --git a/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.result.ts b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.result.ts new file mode 100644 index 0000000..59bfc04 --- /dev/null +++ b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.result.ts @@ -0,0 +1,10 @@ +/** + * @description + * @return default {void} + * @param a {any} + * @param b {any} + */ +const func = (a, b) => { + console.log(a + b); + return a + b; +}; diff --git a/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.ts b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.ts new file mode 100644 index 0000000..59bfc04 --- /dev/null +++ b/sampleWorkspace/function-comment/variable-arrow-function-without-params-type.ts @@ -0,0 +1,10 @@ +/** + * @description + * @return default {void} + * @param a {any} + * @param b {any} + */ +const func = (a, b) => { + console.log(a + b); + return a + b; +}; diff --git a/src/commands/addFileheader.ts b/src/commands/addFileheader.ts index 09a7a13..2633ac7 100644 --- a/src/commands/addFileheader.ts +++ b/src/commands/addFileheader.ts @@ -7,9 +7,11 @@ export const addFileheader = (): Command => { return { name: 'turboFileHeader.addFileheader', handler: async (_context: vscode.ExtensionContext, args?: unknown[]) => { - const { activeEditor = vscode.window.activeTextEditor } = (args ?? [])[0] as { - activeEditor?: vscode.TextEditor; - }; + let activeEditor: vscode.TextEditor | undefined; + if (args && args[0]) { + activeEditor = (args[0] as { activeEditor?: vscode.TextEditor }).activeEditor; + } + activeEditor = activeEditor ?? vscode.window.activeTextEditor; // if no active window is open, return if (!activeEditor) { return; diff --git a/src/commands/addFunctionComment.ts b/src/commands/addFunctionComment.ts index d15a5b1..9959f0a 100644 --- a/src/commands/addFunctionComment.ts +++ b/src/commands/addFunctionComment.ts @@ -7,9 +7,11 @@ export const addFunctionComment = (): Command => { return { name: 'turboFileHeader.addFunctionComment', handler: async (_context: vscode.ExtensionContext, args?: unknown[]) => { - const { activeEditor = vscode.window.activeTextEditor } = (args ?? [])[0] as { - activeEditor?: vscode.TextEditor; - }; + let activeEditor: vscode.TextEditor | undefined; + if (args && args[0]) { + activeEditor = (args[0] as { activeEditor?: vscode.TextEditor }).activeEditor; + } + activeEditor = activeEditor ?? vscode.window.activeTextEditor; if (!activeEditor) { return; diff --git a/src/configuration/ConfigReader.ts b/src/configuration/ConfigReader.ts index a99984d..e1b0618 100644 --- a/src/configuration/ConfigReader.ts +++ b/src/configuration/ConfigReader.ts @@ -5,8 +5,8 @@ import YAML from 'yaml'; import { CUSTOM_CONFIG_FILE_NAME } from '@/constants'; import { CustomError, ErrorCode } from '@/error'; -import { ConfigYaml } from '@/typings/types'; import { logger } from '@/extension'; +import { ConfigYaml } from '@/typings/types'; export class ConfigReader { private static instance: ConfigReader; diff --git a/src/fileheader/FileheaderManager.ts b/src/fileheader/FileheaderManager.ts index c90f657..f54cc43 100644 --- a/src/fileheader/FileheaderManager.ts +++ b/src/fileheader/FileheaderManager.ts @@ -50,11 +50,7 @@ export class FileheaderManager { public async updateFileheader( document: vscode.TextDocument, - { - allowInsert = true, - addSelection = false, - newFile = false, - }: UpdateFileheaderManagerOptions = {}, + { allowInsert = true, addSelection = false, newFile = false }: UpdateFileheaderManagerOptions = {}, ) { const config = this.configManager.getConfiguration(); const provider = await findProvider(this.configManager, this.providers, document); @@ -64,11 +60,7 @@ export class FileheaderManager { return; } - const originFileheaderInfo = getOriginFileheaderInfo( - document, - provider, - config.patternMultiline, - ); + const originFileheaderInfo = getOriginFileheaderInfo(document, provider, config.patternMultiline); let fileheaderVariable: IFileheaderVariables; try { fileheaderVariable = await this.fileheaderVariableBuilder?.build( @@ -125,9 +117,7 @@ export class FileheaderManager { updateProgress(progress, processedFiles, totalFiles); } catch (error) { reprocessedFiles.push(file); - logger.handleError( - new CustomError(ErrorCode.UpdateFileHeaderFail, file.fsPath, error), - ); + logger.handleError(new CustomError(ErrorCode.UpdateFileHeaderFail, file.fsPath, error)); } } @@ -173,10 +163,7 @@ export class FileheaderManager { const range = parser?.getOriginFunctionCommentRange(comments, document, insertPosition); // 原来有函数注释 if (range) { - const originFunctionInfo: FunctionCommentInfo = parser?.parseFunctionComment( - document, - range, - ) || { + const originFunctionInfo: FunctionCommentInfo = parser?.parseFunctionComment(document, range) || { paramsInfo: {}, returnInfo: { default: { type: '', description: '' } }, descriptionInfo: '', @@ -189,7 +176,7 @@ export class FileheaderManager { if (functionCommentInfo) { const originFunctionComment = generateFunctionComment(originFunctionInfo); const functionComment = generateFunctionComment(functionCommentInfo); - // 函数注释有变化 + // 函数注释没有变化 if (originFunctionComment === functionComment) { logger.info('Not need update function comment:', document.uri.fsPath); return false; diff --git a/src/function-params-parser/FunctionParamsParser.ts b/src/function-params-parser/FunctionParamsParser.ts index 5ab053d..737ee56 100644 --- a/src/function-params-parser/FunctionParamsParser.ts +++ b/src/function-params-parser/FunctionParamsParser.ts @@ -68,12 +68,7 @@ export abstract class FunctionParamsParser { const lineText = line.text; // 更新块注释的开始和结束状态 - isInsideBlockComment = updateBlockCommentState( - comments, - lineText, - isInsideBlockComment, - 'up', - ); + isInsideBlockComment = updateBlockCommentState(comments, lineText, isInsideBlockComment, 'up'); // 判断当前行是否是注释行 if (isCommentLine(comments, lineText, isInsideBlockComment)) { startPosition = document.lineAt(i).range.start; @@ -81,11 +76,8 @@ export abstract class FunctionParamsParser { // 不在块注释中 if (!isInsideBlockComment) { // 如果有多个空行,在往前最后一个空行 break - if ( - line.isEmptyOrWhitespace && - i - 1 >= 0 && - !document.lineAt(i - 1).isEmptyOrWhitespace - ) { + if (line.isEmptyOrWhitespace && i - 1 >= 0 && !document.lineAt(i - 1).isEmptyOrWhitespace) { + startPosition = document.lineAt(i + 1).range.start; break; } // 如果当前行不是空行,结束循环 @@ -100,10 +92,7 @@ export abstract class FunctionParamsParser { return range; } - public parseFunctionComment( - document: vscode.TextDocument, - range: vscode.Range, - ): FunctionCommentInfo { + public parseFunctionComment(document: vscode.TextDocument, range: vscode.Range): FunctionCommentInfo { const descriptionPattern = /@description\s+(.*)/; const paramPattern = /@param\s+(\w+)\s*\{(.+?)\}\s*(.*)/; const returnPattern = /@return\s+(?:(\w+)\s*)?\{(.+?)\}\s*(.*)/; @@ -116,10 +105,10 @@ export abstract class FunctionParamsParser { for (const line of functionCommentLines) { let match; if ((match = paramPattern.exec(line)) !== null) { - const [_, name, type, description = ''] = match; + const [_, name, type = 'any', description = ''] = match; paramsInfo[name] = { type, description }; } else if ((match = returnPattern.exec(line)) !== null) { - const [_, key = 'default', type, description = ''] = match; + const [_, key = 'default', type = 'any', description = ''] = match; returnInfo[key] = { type, description }; } else if ((match = descriptionPattern.exec(line)) !== null) { const [_, description] = match; diff --git a/src/function-params-parser/TypescriptProvider.ts b/src/function-params-parser/TypescriptProvider.ts index c53e8f9..2fedcf5 100644 --- a/src/function-params-parser/TypescriptProvider.ts +++ b/src/function-params-parser/TypescriptProvider.ts @@ -18,16 +18,16 @@ function splitParams(paramsStr: string): ParamsInfo { } else if (char === ',' && bracketCount === 0) { const paramStr = paramsStr.slice(paramStartIndex, i); const colonIndex = paramStr.indexOf(':'); - const name = paramStr.slice(0, colonIndex).trim(); - const type = paramStr.slice(colonIndex + 1).trim(); + const name = paramStr.slice(0, colonIndex !== -1 ? colonIndex : paramStr.length).trim(); + const type = colonIndex !== -1 ? paramStr.slice(colonIndex + 1).trim() : 'any'; params[name] = { type, description: '' }; paramStartIndex = i + 1; } } const paramStr = paramsStr.slice(paramStartIndex); const colonIndex = paramStr.indexOf(':'); - const name = paramStr.slice(0, colonIndex).trim(); - const type = paramStr.slice(colonIndex + 1).trim(); + const name = paramStr.slice(0, colonIndex !== -1 ? colonIndex : paramStr.length).trim(); + const type = colonIndex !== -1 ? paramStr.slice(colonIndex + 1).trim() : 'any'; params[name] = { type, description: '' }; return params; } diff --git a/src/test/suite/addFileheader.test.ts b/src/test/suite/addFileheader.test.ts index d365343..1ab1972 100644 --- a/src/test/suite/addFileheader.test.ts +++ b/src/test/suite/addFileheader.test.ts @@ -3,25 +3,34 @@ import { describe, it } from 'mocha'; import { executeCommandOnFile } from './common/executeCommandOnFile'; +const testInfo = [ + { + workspaceName: 'file-header', + files: [ + { fileName: 'no-fileheader.js', cursorLine: 0 }, + { fileName: 'no-fileheader.ts', cursorLine: 0 }, + { fileName: 'no-fileheader.go', cursorLine: 0 }, + ], + }, +]; + describe('Extension Integration Test: addFileheader', function () { this.timeout(20000); - it('should add file header for [.ts]', async () => { - const commandName = 'turboFileHeader.addFileheader'; - const workspaceName = 'file-header'; - const fileName = 'no-fileheader.ts'; - const { actual } = await executeCommandOnFile(commandName, workspaceName, fileName, false); - // 有 Copyright 字符串即可 - assert.notEqual(actual.indexOf('Copyright'), -1); - // @description 后面有非空格字符 - assert.match(actual, /@description\s+\S+/); - }); - it('should add file header for [.go]', async () => { - const commandName = 'turboFileHeader.addFileheader'; - const workspaceName = 'file-header'; - const fileName = 'no-fileheader.go'; - const { actual } = await executeCommandOnFile(commandName, workspaceName, fileName, false); - assert.notEqual(actual.indexOf('Copyright'), -1); - assert.match(actual, /@description\s+\S+/); + testInfo.forEach((item) => { + const workspaceName = item.workspaceName; + const files = item.files; + files.forEach((fileInfo) => { + const fileName = fileInfo.fileName; + const cursorLine = fileInfo.cursorLine; + it(`should add file header for [${fileName}]`, async () => { + const commandName = 'turboFileHeader.addFileheader'; + const { actual } = await executeCommandOnFile(commandName, workspaceName, fileName, cursorLine, false); + // 有 Copyright 字符串即可 + assert.notEqual(actual.indexOf('Copyright'), -1); + // @description 后面有非空格字符 + assert.match(actual, /@description\s+\S+/); + }); + }); }); }); diff --git a/src/test/suite/addFunctionComment.test.ts b/src/test/suite/addFunctionComment.test.ts index e537f7b..883aea9 100644 --- a/src/test/suite/addFunctionComment.test.ts +++ b/src/test/suite/addFunctionComment.test.ts @@ -1,19 +1,40 @@ import assert from 'assert'; import { describe, it } from 'mocha'; +import path from 'path'; import { getText } from '@/utils/vscode-utils'; import { executeCommandOnFile } from './common/executeCommandOnFile'; +const testInfo = [ + { + workspaceName: 'function-comment', + files: [ + { fileName: 'variable-arrow-function-with-params-type.ts', cursorLine: 0 }, + { fileName: 'variable-arrow-function-without-params-type.ts', cursorLine: 0 }, + { fileName: 'variable-arrow-function-without-params-type.js', cursorLine: 0 }, + { fileName: 'variable-arrow-function-with-params-type-update.ts', cursorLine: 6 }, + ], + }, +]; + describe('Extension Integration Test: addFunctionComment', function () { this.timeout(20000); - it('should add function comment for [.ts]', async () => { - const commandName = 'turboFileHeader.addFunctionComment'; - const workspaceName = 'function-comment'; - const fileName = 'variable-arrow-function-with-params-type.ts'; - const resultFileName = 'variable-arrow-function-with-params-type.result.ts'; - const { actual } = await executeCommandOnFile(commandName, workspaceName, fileName, false); - const expected = await getText(workspaceName, resultFileName); - assert.equal(actual, expected); + + testInfo.forEach((item) => { + const workspaceName = item.workspaceName; + const files = item.files; + files.forEach((fileInfo) => { + const fileName = fileInfo.fileName; + const cursorLine = fileInfo.cursorLine; + it(`should add function comment for [.ts] in file ${fileName}`, async () => { + const commandName = 'turboFileHeader.addFunctionComment'; + const ext = path.extname(fileName); + const resultFileName = fileName.replace(ext, `.result${ext}`); + const { actual } = await executeCommandOnFile(commandName, workspaceName, fileName, cursorLine, false); + const expected = await getText(workspaceName, resultFileName); + assert.equal(actual, expected); + }); + }); }); }); diff --git a/src/test/suite/common/executeCommandOnFile.ts b/src/test/suite/common/executeCommandOnFile.ts index 2d0316c..45a8fec 100644 --- a/src/test/suite/common/executeCommandOnFile.ts +++ b/src/test/suite/common/executeCommandOnFile.ts @@ -3,7 +3,7 @@ import path from 'path'; import * as vscode from 'vscode'; import { sleep } from '@/utils/utils'; -import { getWorkspaceFolderUriByName, setActiveWorkspaceByName } from '@/utils/vscode-utils'; +import { getWorkspaceFolderUriByName } from '@/utils/vscode-utils'; /** * execute command on file @@ -16,6 +16,7 @@ export async function executeCommandOnFile( commandName: string, workspaceFolderName: string, srcFileName: string, + cursorLine: number, shouldRetry = false, ) { const ext = path.extname(srcFileName); @@ -29,6 +30,13 @@ export async function executeCommandOnFile( // 打开文件 const doc = await vscode.workspace.openTextDocument(testAbsPath); await vscode.window.showTextDocument(doc); + // 定位光标行,行从 0 开始 + if (cursorLine > 0) { + const editor = vscode.window.activeTextEditor; + if (editor) { + editor.selection = new vscode.Selection(cursorLine, 0, cursorLine, 0); + } + } // 执行之前获取文件内容 const originText = doc.getText();