Skip to content

Commit

Permalink
feat: 函数注释支持 c
Browse files Browse the repository at this point in the history
Co-authored-by: ygqygq2 <ygqygq2@qq.com>
  • Loading branch information
ygqygq2 committed May 12, 2024
1 parent c0c2ddb commit f6658f3
Show file tree
Hide file tree
Showing 15 changed files with 313 additions and 31 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

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

# [0.2.8]

## 新增功能 🌱

- feat: 函数注释支持 c

## 问题修复 🐛

- fix: 修复 paramNameBeforeType 解构时未设置默认值引起问题

# [0.2.7]

## 新增功能 🌱
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "turbo-file-header",
"displayName": "Turbo File Header",
"description": "%description%",
"version": "0.2.7",
"version": "0.2.8",
"icon": "resources/icons/icon.png",
"repository": {
"type": "git",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
void func() {
printf("test\n");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @description
* @return default {void}
*/
void func() {
printf("test\n");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
int func(char* a, int b) {
printf("test\n");
return 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @description
* @return default {int}
* @param a {char*}
* @param b {int}
*/
int func(char* a, int b) {
printf("test\n");
return 1;
}
1 change: 1 addition & 0 deletions sampleWorkspace/test.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
{ "path": "function-comment-for-python" },
{ "path": "function-comment-for-php" },
{ "path": "function-comment-for-rust" },
{ "path": "function-comment-for-c" },
{ "path": "workspace" },
],
"settings": {},
Expand Down
164 changes: 164 additions & 0 deletions src/function-params-parser/CProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
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 { FunctionParamsParser } from './FunctionParamsParser';
import { splitParams } from './c-splitParams';
import { extractFunctionParamsString } from './extractFunctionParamsString';
import { FunctionParamsInfo, ParamsInfo, ReturnInfo } from './types';

function matchNormalFunction(
functionDefinition: string,
languageSettings: LanguageFunctionCommentSettings,
): {
matched: boolean;
returnType: ReturnInfo;
params: ParamsInfo;
} {
const { defaultReturnName = 'default' } = languageSettings;
const returnType: ReturnInfo = {};
let matched = false;
let params: ParamsInfo = {};

// 提取参数括号里的字符串
const functionParamsStr = extractFunctionParamsString(functionDefinition);
const functionParamsRegStr = escapeRegexString(functionParamsStr);
const functionPattern = new RegExp(
`(.*?)\\s+([a-zA-Z0-9_]+)\\s*\\(${functionParamsRegStr}\\)\\s*{[\\s\\S]*?}`,
'm',
);

const match = functionPattern.exec(functionDefinition);
const modifiers = ['static', 'inline'];

if (match) {
matched = true;
const prefixParts = match[1].split(' ').filter((part) => part !== '');
let returnTypeIndex = 0;

for (let i = 0; i < prefixParts.length; i++) {
if (!modifiers.includes(prefixParts[i])) {
returnTypeIndex = i;
break;
}
}

const returnTypeStr = prefixParts.slice(returnTypeIndex).join(' ');
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 = 'auto' } = languageSettings;
let returnType: ReturnInfo = {
[defaultReturnName]: { type: defaultReturnType, description: '' },
};
let matched = false;
let params: ParamsInfo = {};

const matchers = [matchNormalFunction];

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 CParser 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 === '*/') {
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),
};
}
}
79 changes: 50 additions & 29 deletions src/function-params-parser/FunctionParamsParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export abstract class FunctionParamsParser {
document: vscode.TextDocument,
range: vscode.Range,
): FunctionCommentInfo {
const { paramNameBeforeType } = this.languageSettings;
const { paramNameBeforeType = true } = this.languageSettings;
const functionCommentLines = document.getText(range).split('\n');

return paramNameBeforeType
Expand All @@ -154,8 +154,7 @@ export abstract class FunctionParamsParser {
protected parseFunctionCommentNameFirst(functionCommentLines: string[]): FunctionCommentInfo {
const paramPattern =
/@param\s+(?:\[\s*([^=\]]+)(?:=(.*?))?\s*\]|([^=\]]+))\s*\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/;
const returnPattern = /@return\s+(?:(\w+)\s*)?\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/;

const returnPattern = /@return\s+(\w+)\s*\{((?:[^}]|\}(?!\s*$))*)\}\s*(.*)/;
return this.parseFunctionCommentLines(functionCommentLines, paramPattern, returnPattern, true);
}

Expand All @@ -182,39 +181,61 @@ export abstract class FunctionParamsParser {
defaultParamType = 'any',
} = this.languageSettings;
const descriptionPattern = /@description\s+(.*)/;

const handleParamNameBeforeType = (match: RegExpExecArray) => {
const [
_,
optionalName,
defaultValue,
name,
type = defaultParamType as string,
description = '',
] = match;
const realName = optionalName || name;
paramsInfo[realName] = {
type,
description,
...(defaultValue && { defaultValue }),
...(!defaultValue && optionalName && { optional: true }),
};
};

const handleParamTypeBeforeName = (match: RegExpExecArray) => {
const [
_,
type = defaultParamType as string,
optionalName,
defaultValue,
name,
description = '',
] = match;
const realName = optionalName || name;
paramsInfo[realName] = {
type,
description,
...(defaultValue && { defaultValue }),
...(!defaultValue && optionalName && { optional: true }),
};
};

const handleReturn = (match: RegExpExecArray) => {
let [_, name, type, description = ''] = match;
if (!paramNameBeforeType) {
[_, type, name, description = ''] = match;
}
returnInfo[name || defaultReturnName] = { type: type || defaultReturnType, description };
};

for (const line of functionCommentLines) {
let match;
if ((match = paramPattern.exec(line)) !== null) {
let _, optionalName, defaultValue, name, type, description;
if (paramNameBeforeType) {
[
_,
optionalName,
defaultValue,
name,
type = defaultParamType as string,
description = '',
] = match;
handleParamNameBeforeType(match);
} else {
[
_,
type = defaultParamType as string,
optionalName,
defaultValue,
name,
description = '',
] = match;
handleParamTypeBeforeName(match);
}
const realName = optionalName || name;
paramsInfo[realName] = {
type,
description,
...(defaultValue && { defaultValue }),
...(!defaultValue && optionalName && { optional: true }),
};
} else if ((match = returnPattern.exec(line)) !== null) {
const [_, key = defaultReturnName, type = defaultReturnType, description = ''] = match;
returnInfo[key] = { type, description };
handleReturn(match);
} else if ((match = descriptionPattern.exec(line)) !== null) {
const [_, description] = match;
descriptionInfo = description.trim();
Expand Down
3 changes: 3 additions & 0 deletions src/function-params-parser/FunctionParserLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ConfigManager } from '@/configuration/ConfigManager';
import { CustomError, ErrorCode } from '@/error';
import { logger } from '@/extension';

import { CParser } from './CProvider';
import { FunctionParamsParser } from './FunctionParamsParser';
import { GoParser } from './GoProvider';
import { JavaParser } from './JavaProvider';
Expand Down Expand Up @@ -30,11 +31,13 @@ export class FunctionParserLoader {
typescriptreact: TypescriptParser,
javascript: JavascriptParser,
javascriptreact: JavascriptParser,
vue: TypescriptParser,
go: GoParser,
java: JavaParser,
python: PythonParser,
php: PhpParser,
rust: RustParser,
c: CParser,
};

public async loadParser(languageId: string): Promise<FunctionParamsParser | null> {
Expand Down
Loading

0 comments on commit f6658f3

Please sign in to comment.