From 360416cc7448b7a818196529513b741867da347f Mon Sep 17 00:00:00 2001 From: ygqygq2 Date: Sun, 28 Apr 2024 14:41:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=87=BD=E6=95=B0=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ygqygq2 --- CHANGELOG.md | 6 + package.json | 4 +- .../function-none-params-with-return.php | 3 + ...unction-none-params-with-return.result.php | 7 + .../function-none-params-without-return.php | 3 + ...tion-none-params-without-return.result.php | 7 + .../function-optional-params-with-return.php | 3 + ...ion-optional-params-with-return.result.php | 10 + .../function-with-params-with-return.php | 3 + ...unction-with-params-with-return.result.php | 9 + .../function-with-params-without-return.php | 3 + ...tion-with-params-without-return.result.php | 9 + .../function-with-params-with-return.py | 2 + ...function-with-params-with-return.result.py | 16 +- .../function-with-params-without-return.py | 2 +- ...ction-with-params-without-return.result.py | 4 +- sampleWorkspace/test.code-workspace | 1 + .../FunctionParamsParser.ts | 7 +- .../FunctionParserLoader.ts | 2 + src/function-params-parser/PhpProvider.ts | 194 ++++++++++++++++++ src/function-params-parser/php-splitParams.ts | 47 +++++ src/test/suite/php-functionComment.test.ts | 18 ++ src/test/suite/python-functionComment.test.ts | 1 + 23 files changed, 344 insertions(+), 17 deletions(-) create mode 100644 sampleWorkspace/function-comment-for-php/function-none-params-with-return.php create mode 100644 sampleWorkspace/function-comment-for-php/function-none-params-with-return.result.php create mode 100644 sampleWorkspace/function-comment-for-php/function-none-params-without-return.php create mode 100644 sampleWorkspace/function-comment-for-php/function-none-params-without-return.result.php create mode 100644 sampleWorkspace/function-comment-for-php/function-optional-params-with-return.php create mode 100644 sampleWorkspace/function-comment-for-php/function-optional-params-with-return.result.php create mode 100644 sampleWorkspace/function-comment-for-php/function-with-params-with-return.php create mode 100644 sampleWorkspace/function-comment-for-php/function-with-params-with-return.result.php create mode 100644 sampleWorkspace/function-comment-for-php/function-with-params-without-return.php create mode 100644 sampleWorkspace/function-comment-for-php/function-with-params-without-return.result.php create mode 100644 sampleWorkspace/function-comment-for-python/function-with-params-with-return.py create mode 100644 src/function-params-parser/PhpProvider.ts create mode 100644 src/function-params-parser/php-splitParams.ts create mode 100644 src/test/suite/php-functionComment.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8405298..afece2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to the "turbo-file-header" extension will be documented in this file. +# [0.2.6] + +## 新增功能 🌱 + +- feat: 函数注释支持 php + # [0.2.5] ## 新增功能 🌱 diff --git a/package.json b/package.json index 2ccf164..86ac592 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "turbo-file-header", "displayName": "Turbo File Header", "description": "%description%", - "version": "0.2.5", + "version": "0.2.6", "icon": "resources/icons/icon.png", "repository": { "type": "git", @@ -337,7 +337,7 @@ }, "defaultParamType": { "type": "string", - "default": "any", + "default": "", "description": "%turboFileHeader.functionComment.languagesSettings.items.properties.defaultParamType.description%" }, "typesUsingDefaultReturnType": { diff --git a/sampleWorkspace/function-comment-for-php/function-none-params-with-return.php b/sampleWorkspace/function-comment-for-php/function-none-params-with-return.php new file mode 100644 index 0000000..add3cf0 --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-none-params-with-return.php @@ -0,0 +1,3 @@ +function funcName (): String { + return "test" +} diff --git a/sampleWorkspace/function-comment-for-php/function-none-params-with-return.result.php b/sampleWorkspace/function-comment-for-php/function-none-params-with-return.result.php new file mode 100644 index 0000000..a9caaba --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-none-params-with-return.result.php @@ -0,0 +1,7 @@ +/** + * @description + * @return default {String} + */ +function funcName (): String { + return "test" +} diff --git a/sampleWorkspace/function-comment-for-php/function-none-params-without-return.php b/sampleWorkspace/function-comment-for-php/function-none-params-without-return.php new file mode 100644 index 0000000..20d7fa4 --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-none-params-without-return.php @@ -0,0 +1,3 @@ +function funcName () { + return "test" +} diff --git a/sampleWorkspace/function-comment-for-php/function-none-params-without-return.result.php b/sampleWorkspace/function-comment-for-php/function-none-params-without-return.result.php new file mode 100644 index 0000000..762057d --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-none-params-without-return.result.php @@ -0,0 +1,7 @@ +/** + * @description + * @return default {auto} + */ +function funcName () { + return "test" +} diff --git a/sampleWorkspace/function-comment-for-php/function-optional-params-with-return.php b/sampleWorkspace/function-comment-for-php/function-optional-params-with-return.php new file mode 100644 index 0000000..7f28b8d --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-optional-params-with-return.php @@ -0,0 +1,3 @@ +function funcName(int $a, $b = 5, ...$rest): int { + return a + b; +} diff --git a/sampleWorkspace/function-comment-for-php/function-optional-params-with-return.result.php b/sampleWorkspace/function-comment-for-php/function-optional-params-with-return.result.php new file mode 100644 index 0000000..ac0affc --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-optional-params-with-return.result.php @@ -0,0 +1,10 @@ +/** + * @description + * @return default {int} + * @param $a {int} + * @param [$b=5] {any} + * @param [$rest] {any} + */ +function funcName(int $a, $b = 5, ...$rest): int { + return a + b; +} diff --git a/sampleWorkspace/function-comment-for-php/function-with-params-with-return.php b/sampleWorkspace/function-comment-for-php/function-with-params-with-return.php new file mode 100644 index 0000000..9bb1e1b --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-with-params-with-return.php @@ -0,0 +1,3 @@ +function funcName(int $a, int $b): int { + return a + b; +} diff --git a/sampleWorkspace/function-comment-for-php/function-with-params-with-return.result.php b/sampleWorkspace/function-comment-for-php/function-with-params-with-return.result.php new file mode 100644 index 0000000..a7a4dd5 --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-with-params-with-return.result.php @@ -0,0 +1,9 @@ +/** + * @description + * @return default {int} + * @param $a {int} + * @param $b {int} + */ +function funcName(int $a, int $b): int { + return a + b; +} diff --git a/sampleWorkspace/function-comment-for-php/function-with-params-without-return.php b/sampleWorkspace/function-comment-for-php/function-with-params-without-return.php new file mode 100644 index 0000000..4279c5f --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-with-params-without-return.php @@ -0,0 +1,3 @@ +function funcName(int $a, int $b) { + return a + b; +} diff --git a/sampleWorkspace/function-comment-for-php/function-with-params-without-return.result.php b/sampleWorkspace/function-comment-for-php/function-with-params-without-return.result.php new file mode 100644 index 0000000..a9654fb --- /dev/null +++ b/sampleWorkspace/function-comment-for-php/function-with-params-without-return.result.php @@ -0,0 +1,9 @@ +/** + * @description + * @return default {auto} + * @param $a {int} + * @param $b {int} + */ +function funcName(int $a, int $b) { + return a + b; +} diff --git a/sampleWorkspace/function-comment-for-python/function-with-params-with-return.py b/sampleWorkspace/function-comment-for-python/function-with-params-with-return.py new file mode 100644 index 0000000..7683944 --- /dev/null +++ b/sampleWorkspace/function-comment-for-python/function-with-params-with-return.py @@ -0,0 +1,2 @@ +def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: + return a + b diff --git a/sampleWorkspace/function-comment-for-python/function-with-params-with-return.result.py b/sampleWorkspace/function-comment-for-python/function-with-params-with-return.result.py index cc1e219..abfa53e 100644 --- a/sampleWorkspace/function-comment-for-python/function-with-params-with-return.result.py +++ b/sampleWorkspace/function-comment-for-python/function-with-params-with-return.result.py @@ -1,10 +1,8 @@ -/** +""" * @description - * @return default {int} - * @param String {a} - * @param int {b} - */ -public int func(String a, int b) { - System.out.println("test"); - return 1; -} + * @return default {Union[int, float]} + * @param a {Union[int, float]} + * @param b {Union[int, float]} +""" +def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: + return a + b diff --git a/sampleWorkspace/function-comment-for-python/function-with-params-without-return.py b/sampleWorkspace/function-comment-for-python/function-with-params-without-return.py index 7683944..859aa27 100644 --- a/sampleWorkspace/function-comment-for-python/function-with-params-without-return.py +++ b/sampleWorkspace/function-comment-for-python/function-with-params-without-return.py @@ -1,2 +1,2 @@ -def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: +def add(a: Union[int, float], b: Union[int, float]): return a + b diff --git a/sampleWorkspace/function-comment-for-python/function-with-params-without-return.result.py b/sampleWorkspace/function-comment-for-python/function-with-params-without-return.result.py index abfa53e..e28e7a5 100644 --- a/sampleWorkspace/function-comment-for-python/function-with-params-without-return.result.py +++ b/sampleWorkspace/function-comment-for-python/function-with-params-without-return.result.py @@ -1,8 +1,8 @@ """ * @description - * @return default {Union[int, float]} + * @return default {auto} * @param a {Union[int, float]} * @param b {Union[int, float]} """ -def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: +def add(a: Union[int, float], b: Union[int, float]): return a + b diff --git a/sampleWorkspace/test.code-workspace b/sampleWorkspace/test.code-workspace index 46361bd..2b8996a 100644 --- a/sampleWorkspace/test.code-workspace +++ b/sampleWorkspace/test.code-workspace @@ -6,6 +6,7 @@ { "path": "function-comment-for-go" }, { "path": "function-comment-for-java" }, { "path": "function-comment-for-python" }, + { "path": "function-comment-for-php" }, { "path": "workspace" }, ], "settings": {}, diff --git a/src/function-params-parser/FunctionParamsParser.ts b/src/function-params-parser/FunctionParamsParser.ts index 59e1a8a..5bef573 100644 --- a/src/function-params-parser/FunctionParamsParser.ts +++ b/src/function-params-parser/FunctionParamsParser.ts @@ -53,8 +53,9 @@ export abstract class FunctionParamsParser { mergedParams[key] = { // 因为默认值无法确认类型,所以以用户修改的类型为准 type: - (params[key]?.defaultValue ? paramsInfo[key]?.type : params[key].type) ?? - params[key].type, + (params[key]?.defaultValue && paramsInfo[key]?.defaultValue + ? paramsInfo[key]?.type + : params[key].type) ?? params[key].type, description: paramsInfo[key]?.description || '', ...(params[key]?.optional && { optional: true }), ...(params[key]?.defaultValue && { defaultValue: params[key].defaultValue }), @@ -144,7 +145,7 @@ export abstract class FunctionParamsParser { ): FunctionCommentInfo { const descriptionPattern = /@description\s+(.*)/; const paramPattern = - /@param\s+(?:\[(\w+)(?:=(.*?))?\]|(\w+))\s*\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/; + /@param\s+(?:\[\s*([^=\]]+)(?:=(.*?))?\s*\]|([^=\]]+))\s*\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/; const returnPattern = /@return\s+(?:(\w+)\s*)?\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/; const functionCommentLines = document.getText(range).split('\n'); diff --git a/src/function-params-parser/FunctionParserLoader.ts b/src/function-params-parser/FunctionParserLoader.ts index fc44d8e..7ac66a4 100644 --- a/src/function-params-parser/FunctionParserLoader.ts +++ b/src/function-params-parser/FunctionParserLoader.ts @@ -6,6 +6,7 @@ import { FunctionParamsParser } from './FunctionParamsParser'; import { GoParser } from './GoProvider'; import { JavaParser } from './JavaProvider'; import { JavascriptParser } from './JavascriptProvider'; +import { PhpParser } from './PhpProvider'; import { PythonParser } from './PythonProvider'; import { TypescriptParser } from './TypescriptProvider'; @@ -31,6 +32,7 @@ export class FunctionParserLoader { go: GoParser, java: JavaParser, python: PythonParser, + php: PhpParser, }; public async loadParser(languageId: string): Promise { diff --git a/src/function-params-parser/PhpProvider.ts b/src/function-params-parser/PhpProvider.ts new file mode 100644 index 0000000..e380477 --- /dev/null +++ b/src/function-params-parser/PhpProvider.ts @@ -0,0 +1,194 @@ +import * as vscode from 'vscode'; + +import { ConfigManager } from '@/configuration/ConfigManager'; +import { logger } from '@/extension'; +import { LanguageFunctionCommentSettings } from '@/typings/types'; +import { escapeRegexString } from '@/utils/str'; + +import { extractFunctionParamsString } from './extractFunctionParamsString'; +import { FunctionParamsParser } from './FunctionParamsParser'; +import { splitParams } from './php-splitParams'; +import { FunctionParamsInfo, ParamsInfo, ReturnInfo } from './types'; + +function matchNormalFunction( + functionDefinition: string, + languageSettings: LanguageFunctionCommentSettings, +): { + matched: boolean; + returnType: ReturnInfo; + params: ParamsInfo; +} { + const { defaultReturnName = 'default', defaultReturnType = 'Null' } = languageSettings; + const returnType: ReturnInfo = {}; + let matched = false; + let params: ParamsInfo = {}; + + // 提取参数括号里的字符串 + const functionParamsStr = extractFunctionParamsString(functionDefinition); + const functionParamsRegStr = escapeRegexString(functionParamsStr); + const functionPattern = new RegExp( + `function\\s*([&?]?[a-zA-Z0-9_]+)\\s*\\(${functionParamsRegStr}\\)\\s*(?::\\s*(.*))?\\s*{`, + 'm', + ); + + const match = functionPattern.exec(functionDefinition); + + if (match) { + matched = true; + const returnTypeStr = match[2] ? match[2].trim() : defaultReturnType; + + returnType[defaultReturnName] = { + type: returnTypeStr, + description: '', + }; + + params = splitParams(functionParamsStr, languageSettings); + } + + return { matched, returnType, params }; +} + +function matchArrowFunction( + functionDefinition: string, + languageSettings: LanguageFunctionCommentSettings, +): { + matched: boolean; + returnType: ReturnInfo; + params: ParamsInfo; +} { + const { defaultReturnName = 'default', defaultReturnType = 'Null' } = languageSettings; + const returnType: ReturnInfo = {}; + let matched = false; + let params: ParamsInfo = {}; + + // 提取参数括号里的字符串 + const functionParamsStr = extractFunctionParamsString(functionDefinition); + const functionParamsRegStr = escapeRegexString(functionParamsStr); + const functionPattern = new RegExp(`fn\\s*\\(${functionParamsRegStr}\\)\\s*=>`, 'm'); + + const match = functionPattern.exec(functionDefinition); + + if (match) { + matched = true; + const returnTypeStr = match[2] ? match[2].trim() : defaultReturnType; + + returnType[defaultReturnName] = { + type: returnTypeStr, + description: '', + }; + + params = splitParams(functionParamsStr, languageSettings); + } + + return { matched, returnType, params }; +} + +/** + * @description + * @return default {auto} + */ +function matchFunction( + functionDefinition: string, + languageSettings: LanguageFunctionCommentSettings, +): { matched: boolean; returnType: ReturnInfo; params: ParamsInfo } { + const { defaultReturnName = 'default', defaultReturnType = 'Null' } = languageSettings; + let returnType: ReturnInfo = { + [defaultReturnName]: { type: defaultReturnType, description: '' }, + }; + let matched = false; + let params: ParamsInfo = {}; + + const matchers = [matchNormalFunction, matchArrowFunction]; + + for (const matcher of matchers) { + const result = matcher(functionDefinition, languageSettings); + if (result.matched) { + matched = result.matched; + params = result.params; + returnType = result.returnType; + break; + } + } + + return { matched, returnType, params }; +} + +export class PhpParser extends FunctionParamsParser { + constructor(configManager: ConfigManager, languageId: string) { + super(configManager, languageId); + } + + private getFunctionString(document: vscode.TextDocument, startLine: number) { + let functionDefinition = ''; + let bracketCount = 0; // 大括号计数 + let parenthesisCount = 0; // 小括号计数 + + for (let i = startLine; i < document.lineCount; i++) { + const line = document.lineAt(i); + functionDefinition += line.text + '\n'; + + for (const char of line.text) { + if (char === '(') { + parenthesisCount++; + } else if (char === ')') { + parenthesisCount--; + } else if (char === '{') { + bracketCount++; + } else if (char === '}') { + bracketCount--; + } + } + + if (bracketCount === 0 && parenthesisCount === 0) { + break; + } + } + + return functionDefinition; + } + + public getFunctionParamsAtCursor( + activeEditor: vscode.TextEditor, + languageSettings: LanguageFunctionCommentSettings = this.languageSettings, + ): FunctionParamsInfo { + let functionParams: ParamsInfo = {}; + let matchedFunction = false; + let returnType: ReturnInfo = {}; + const document = activeEditor.document; + const cursorLine = activeEditor.selection.start.line; + let startLine = cursorLine; + // 如果光标所在行为空行或者注释,则从下一行开始 + const cursorLineText = document.lineAt(cursorLine).text.trim(); + if ( + cursorLineText === '' || + cursorLineText === '//' || + cursorLineText === '#' || + cursorLineText === '*/' + ) { + startLine = cursorLine + 1; + } + + const functionDefinition = this.getFunctionString(document, startLine); + const { + matched, + returnType: returnTypeTmp, + params, + } = matchFunction(functionDefinition, languageSettings); + if (matched) { + matchedFunction = true; + returnType = returnTypeTmp; + functionParams = params; + } + + if (!matchFunction) { + logger.info(vscode.l10n.t('No function found at the cursor')); + } + + return { + matchedFunction, + returnType, + params: functionParams, + insertPosition: new vscode.Position(startLine, 0), + }; + } +} diff --git a/src/function-params-parser/php-splitParams.ts b/src/function-params-parser/php-splitParams.ts new file mode 100644 index 0000000..dee0baf --- /dev/null +++ b/src/function-params-parser/php-splitParams.ts @@ -0,0 +1,47 @@ +import { LanguageFunctionCommentSettings } from '@/typings/types'; + +import { ParamsInfo } from './types'; + +export function splitParams( + paramsStr: string, + languageSettings: LanguageFunctionCommentSettings, +): ParamsInfo { + const { defaultParamType = 'mixed', defaultReturnName = 'default' } = languageSettings; + let bracketCount = 0; + let paramStartIndex = 0; + let defaultCount = 0; + const params: ParamsInfo = {}; + for (let i = 0; i < paramsStr?.length; i++) { + const char = paramsStr[i]; + if (char === '(' || char === '[' || char === '{' || char === '<') { + bracketCount++; + } else if (char === ')' || char === ']' || char === '}' || char === '>') { + bracketCount--; + } + + if ( + (char === ',' && bracketCount === 0) || + (i === paramsStr.length - 1 && bracketCount === 0) + ) { + const paramStr = paramsStr + .slice(paramStartIndex, i === paramsStr.length - 1 ? i + 1 : i) + .trim(); + + const paramPattern = /^(\.\.\.)?(\w*\s*)?(\$\w+)\s*(=\s*(.*))?$/; + const match = paramPattern.exec(paramStr); + if (match) { + const name = + match[3] || + (defaultCount > 0 ? `${defaultReturnName}${defaultCount++}` : defaultReturnName); + const type = match[2]?.trim() || defaultParamType; + const optional = match[1] || match[4] ? { optional: true } : {}; + const defaultValue = match[5] ? { defaultValue: match[5] } : {}; + + params[name] = { type, description: '', ...optional, ...defaultValue }; + } + paramStartIndex = i + 1; + } + } + + return params; +} diff --git a/src/test/suite/php-functionComment.test.ts b/src/test/suite/php-functionComment.test.ts new file mode 100644 index 0000000..f04639b --- /dev/null +++ b/src/test/suite/php-functionComment.test.ts @@ -0,0 +1,18 @@ +import { functionCommentTester } from './common/functionCommentTester'; +import { TestInfo } from './types'; + +const testInfo: TestInfo = [ + { + testName: 'php-function', + workspaceName: 'function-comment-for-php', + files: [ + { fileName: 'function-none-params-with-return.php', cursorLine: 0 }, + { fileName: 'function-none-params-without-return.php', cursorLine: 0 }, + { fileName: 'function-optional-params-with-return.php', cursorLine: 0 }, + { fileName: 'function-with-params-with-return.php', cursorLine: 0 }, + { fileName: 'function-with-params-without-return.php', cursorLine: 0 }, + ], + }, +]; + +functionCommentTester(testInfo); diff --git a/src/test/suite/python-functionComment.test.ts b/src/test/suite/python-functionComment.test.ts index f8c33fa..f3b0a27 100644 --- a/src/test/suite/python-functionComment.test.ts +++ b/src/test/suite/python-functionComment.test.ts @@ -9,6 +9,7 @@ const testInfo: TestInfo = [ { fileName: 'function-none-params-with-return.py', cursorLine: 0 }, { fileName: 'function-none-params-without-return.py', cursorLine: 0 }, { fileName: 'function-optional-params-with-return.py', cursorLine: 0 }, + { fileName: 'function-with-params-with-return.py', cursorLine: 0 }, { fileName: 'function-with-params-without-return.py', cursorLine: 0 }, ], },