Skip to content

Commit f6658f3

Browse files
committed
feat: 函数注释支持 c
Co-authored-by: ygqygq2 <[email protected]>
1 parent c0c2ddb commit f6658f3

15 files changed

+313
-31
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to the "turbo-file-header" extension will be documented in this file.
44

5+
# [0.2.8]
6+
7+
## 新增功能 🌱
8+
9+
- feat: 函数注释支持 c
10+
11+
## 问题修复 🐛
12+
13+
- fix: 修复 paramNameBeforeType 解构时未设置默认值引起问题
14+
515
# [0.2.7]
616

717
## 新增功能 🌱

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "turbo-file-header",
33
"displayName": "Turbo File Header",
44
"description": "%description%",
5-
"version": "0.2.7",
5+
"version": "0.2.8",
66
"icon": "resources/icons/icon.png",
77
"repository": {
88
"type": "git",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
void func() {
2+
printf("test\n");
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* @description
3+
* @return default {void}
4+
*/
5+
void func() {
6+
printf("test\n");
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
int func(char* a, int b) {
2+
printf("test\n");
3+
return 1;
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @description
3+
* @return default {int}
4+
* @param a {char*}
5+
* @param b {int}
6+
*/
7+
int func(char* a, int b) {
8+
printf("test\n");
9+
return 1;
10+
}

sampleWorkspace/test.code-workspace

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
{ "path": "function-comment-for-python" },
99
{ "path": "function-comment-for-php" },
1010
{ "path": "function-comment-for-rust" },
11+
{ "path": "function-comment-for-c" },
1112
{ "path": "workspace" },
1213
],
1314
"settings": {},
+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import * as vscode from 'vscode';
2+
3+
import { ConfigManager } from '@/configuration/ConfigManager';
4+
import { logger } from '@/extension';
5+
import { LanguageFunctionCommentSettings } from '@/typings/types';
6+
import { escapeRegexString } from '@/utils/str';
7+
8+
import { FunctionParamsParser } from './FunctionParamsParser';
9+
import { splitParams } from './c-splitParams';
10+
import { extractFunctionParamsString } from './extractFunctionParamsString';
11+
import { FunctionParamsInfo, ParamsInfo, ReturnInfo } from './types';
12+
13+
function matchNormalFunction(
14+
functionDefinition: string,
15+
languageSettings: LanguageFunctionCommentSettings,
16+
): {
17+
matched: boolean;
18+
returnType: ReturnInfo;
19+
params: ParamsInfo;
20+
} {
21+
const { defaultReturnName = 'default' } = languageSettings;
22+
const returnType: ReturnInfo = {};
23+
let matched = false;
24+
let params: ParamsInfo = {};
25+
26+
// 提取参数括号里的字符串
27+
const functionParamsStr = extractFunctionParamsString(functionDefinition);
28+
const functionParamsRegStr = escapeRegexString(functionParamsStr);
29+
const functionPattern = new RegExp(
30+
`(.*?)\\s+([a-zA-Z0-9_]+)\\s*\\(${functionParamsRegStr}\\)\\s*{[\\s\\S]*?}`,
31+
'm',
32+
);
33+
34+
const match = functionPattern.exec(functionDefinition);
35+
const modifiers = ['static', 'inline'];
36+
37+
if (match) {
38+
matched = true;
39+
const prefixParts = match[1].split(' ').filter((part) => part !== '');
40+
let returnTypeIndex = 0;
41+
42+
for (let i = 0; i < prefixParts.length; i++) {
43+
if (!modifiers.includes(prefixParts[i])) {
44+
returnTypeIndex = i;
45+
break;
46+
}
47+
}
48+
49+
const returnTypeStr = prefixParts.slice(returnTypeIndex).join(' ');
50+
returnType[defaultReturnName] = {
51+
type: returnTypeStr,
52+
description: '',
53+
};
54+
55+
params = splitParams(functionParamsStr, languageSettings);
56+
}
57+
58+
return { matched, returnType, params };
59+
}
60+
61+
/**
62+
* @description
63+
* @return default {auto}
64+
*/
65+
function matchFunction(
66+
functionDefinition: string,
67+
languageSettings: LanguageFunctionCommentSettings,
68+
): { matched: boolean; returnType: ReturnInfo; params: ParamsInfo } {
69+
const { defaultReturnName = 'default', defaultReturnType = 'auto' } = languageSettings;
70+
let returnType: ReturnInfo = {
71+
[defaultReturnName]: { type: defaultReturnType, description: '' },
72+
};
73+
let matched = false;
74+
let params: ParamsInfo = {};
75+
76+
const matchers = [matchNormalFunction];
77+
78+
for (const matcher of matchers) {
79+
const result = matcher(functionDefinition, languageSettings);
80+
if (result.matched) {
81+
matched = result.matched;
82+
params = result.params;
83+
returnType = result.returnType;
84+
break;
85+
}
86+
}
87+
88+
return { matched, returnType, params };
89+
}
90+
91+
export class CParser extends FunctionParamsParser {
92+
constructor(configManager: ConfigManager, languageId: string) {
93+
super(configManager, languageId);
94+
}
95+
96+
private getFunctionString(document: vscode.TextDocument, startLine: number) {
97+
let functionDefinition = '';
98+
let bracketCount = 0; // 大括号计数
99+
let parenthesisCount = 0; // 小括号计数
100+
101+
for (let i = startLine; i < document.lineCount; i++) {
102+
const line = document.lineAt(i);
103+
functionDefinition += line.text + '\n';
104+
105+
for (const char of line.text) {
106+
if (char === '(') {
107+
parenthesisCount++;
108+
} else if (char === ')') {
109+
parenthesisCount--;
110+
} else if (char === '{') {
111+
bracketCount++;
112+
} else if (char === '}') {
113+
bracketCount--;
114+
}
115+
}
116+
117+
if (bracketCount === 0 && parenthesisCount === 0) {
118+
break;
119+
}
120+
}
121+
122+
return functionDefinition;
123+
}
124+
125+
public getFunctionParamsAtCursor(
126+
activeEditor: vscode.TextEditor,
127+
languageSettings: LanguageFunctionCommentSettings = this.languageSettings,
128+
): FunctionParamsInfo {
129+
let functionParams: ParamsInfo = {};
130+
let matchedFunction = false;
131+
let returnType: ReturnInfo = {};
132+
const document = activeEditor.document;
133+
const cursorLine = activeEditor.selection.start.line;
134+
let startLine = cursorLine;
135+
// 如果光标所在行为空行或者注释,则从下一行开始
136+
const cursorLineText = document.lineAt(cursorLine).text.trim();
137+
if (cursorLineText === '' || cursorLineText === '//' || cursorLineText === '*/') {
138+
startLine = cursorLine + 1;
139+
}
140+
141+
const functionDefinition = this.getFunctionString(document, startLine);
142+
const {
143+
matched,
144+
returnType: returnTypeTmp,
145+
params,
146+
} = matchFunction(functionDefinition, languageSettings);
147+
if (matched) {
148+
matchedFunction = true;
149+
returnType = returnTypeTmp;
150+
functionParams = params;
151+
}
152+
153+
if (!matchFunction) {
154+
logger.info(vscode.l10n.t('No function found at the cursor'));
155+
}
156+
157+
return {
158+
matchedFunction,
159+
returnType,
160+
params: functionParams,
161+
insertPosition: new vscode.Position(startLine, 0),
162+
};
163+
}
164+
}

src/function-params-parser/FunctionParamsParser.ts

+50-29
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export abstract class FunctionParamsParser {
143143
document: vscode.TextDocument,
144144
range: vscode.Range,
145145
): FunctionCommentInfo {
146-
const { paramNameBeforeType } = this.languageSettings;
146+
const { paramNameBeforeType = true } = this.languageSettings;
147147
const functionCommentLines = document.getText(range).split('\n');
148148

149149
return paramNameBeforeType
@@ -154,8 +154,7 @@ export abstract class FunctionParamsParser {
154154
protected parseFunctionCommentNameFirst(functionCommentLines: string[]): FunctionCommentInfo {
155155
const paramPattern =
156156
/@param\s+(?:\[\s*([^=\]]+)(?:=(.*?))?\s*\]|([^=\]]+))\s*\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/;
157-
const returnPattern = /@return\s+(?:(\w+)\s*)?\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/;
158-
157+
const returnPattern = /@return\s+(\w+)\s*\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/;
159158
return this.parseFunctionCommentLines(functionCommentLines, paramPattern, returnPattern, true);
160159
}
161160

@@ -182,39 +181,61 @@ export abstract class FunctionParamsParser {
182181
defaultParamType = 'any',
183182
} = this.languageSettings;
184183
const descriptionPattern = /@description\s+(.*)/;
184+
185+
const handleParamNameBeforeType = (match: RegExpExecArray) => {
186+
const [
187+
_,
188+
optionalName,
189+
defaultValue,
190+
name,
191+
type = defaultParamType as string,
192+
description = '',
193+
] = match;
194+
const realName = optionalName || name;
195+
paramsInfo[realName] = {
196+
type,
197+
description,
198+
...(defaultValue && { defaultValue }),
199+
...(!defaultValue && optionalName && { optional: true }),
200+
};
201+
};
202+
203+
const handleParamTypeBeforeName = (match: RegExpExecArray) => {
204+
const [
205+
_,
206+
type = defaultParamType as string,
207+
optionalName,
208+
defaultValue,
209+
name,
210+
description = '',
211+
] = match;
212+
const realName = optionalName || name;
213+
paramsInfo[realName] = {
214+
type,
215+
description,
216+
...(defaultValue && { defaultValue }),
217+
...(!defaultValue && optionalName && { optional: true }),
218+
};
219+
};
220+
221+
const handleReturn = (match: RegExpExecArray) => {
222+
let [_, name, type, description = ''] = match;
223+
if (!paramNameBeforeType) {
224+
[_, type, name, description = ''] = match;
225+
}
226+
returnInfo[name || defaultReturnName] = { type: type || defaultReturnType, description };
227+
};
228+
185229
for (const line of functionCommentLines) {
186230
let match;
187231
if ((match = paramPattern.exec(line)) !== null) {
188-
let _, optionalName, defaultValue, name, type, description;
189232
if (paramNameBeforeType) {
190-
[
191-
_,
192-
optionalName,
193-
defaultValue,
194-
name,
195-
type = defaultParamType as string,
196-
description = '',
197-
] = match;
233+
handleParamNameBeforeType(match);
198234
} else {
199-
[
200-
_,
201-
type = defaultParamType as string,
202-
optionalName,
203-
defaultValue,
204-
name,
205-
description = '',
206-
] = match;
235+
handleParamTypeBeforeName(match);
207236
}
208-
const realName = optionalName || name;
209-
paramsInfo[realName] = {
210-
type,
211-
description,
212-
...(defaultValue && { defaultValue }),
213-
...(!defaultValue && optionalName && { optional: true }),
214-
};
215237
} else if ((match = returnPattern.exec(line)) !== null) {
216-
const [_, key = defaultReturnName, type = defaultReturnType, description = ''] = match;
217-
returnInfo[key] = { type, description };
238+
handleReturn(match);
218239
} else if ((match = descriptionPattern.exec(line)) !== null) {
219240
const [_, description] = match;
220241
descriptionInfo = description.trim();

src/function-params-parser/FunctionParserLoader.ts

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ConfigManager } from '@/configuration/ConfigManager';
22
import { CustomError, ErrorCode } from '@/error';
33
import { logger } from '@/extension';
44

5+
import { CParser } from './CProvider';
56
import { FunctionParamsParser } from './FunctionParamsParser';
67
import { GoParser } from './GoProvider';
78
import { JavaParser } from './JavaProvider';
@@ -30,11 +31,13 @@ export class FunctionParserLoader {
3031
typescriptreact: TypescriptParser,
3132
javascript: JavascriptParser,
3233
javascriptreact: JavascriptParser,
34+
vue: TypescriptParser,
3335
go: GoParser,
3436
java: JavaParser,
3537
python: PythonParser,
3638
php: PhpParser,
3739
rust: RustParser,
40+
c: CParser,
3841
};
3942

4043
public async loadParser(languageId: string): Promise<FunctionParamsParser | null> {

0 commit comments

Comments
 (0)