Skip to content

Commit

Permalink
feat: 函数注释支持 rust
Browse files Browse the repository at this point in the history
Co-authored-by: ygqygq2 <ygqygq2@qq.com>
  • Loading branch information
ygqygq2 committed May 2, 2024
1 parent ffe0e55 commit c0c2ddb
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 1 deletion.
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.6",
"version": "0.2.7",
"icon": "resources/icons/icon.png",
"repository": {
"type": "git",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @description
* @return default {String}
*/
fn foo() -> String {
"a"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn foo() -> String {
"a"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @description
* @return default {auto}
*/
fn foo(x) {
println!("a");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn foo(x) {
println!("a");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @description
* @return default {i32}
* @param bar {Option<i32>}
*/
fn foo(bar: Option<i32>) -> i32 {
let bar = bar.unwrap_or(42);
println!("{}", bar);
bar
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn foo(bar: Option<i32>) -> i32 {
let bar = bar.unwrap_or(42);
println!("{}", bar);
bar
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @description
* @return default {auto}
* @param bar {Option<i32>}
*/
fn foo(bar: Option<i32>) {
let bar = bar.unwrap_or(42);
println!("{}", bar);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn foo(bar: Option<i32>) {
let bar = bar.unwrap_or(42);
println!("{}", bar);
}
1 change: 1 addition & 0 deletions sampleWorkspace/test.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
{ "path": "function-comment-for-java" },
{ "path": "function-comment-for-python" },
{ "path": "function-comment-for-php" },
{ "path": "function-comment-for-rust" },
{ "path": "workspace" },
],
"settings": {},
Expand Down
2 changes: 2 additions & 0 deletions src/function-params-parser/FunctionParserLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { JavaParser } from './JavaProvider';
import { JavascriptParser } from './JavascriptProvider';
import { PhpParser } from './PhpProvider';
import { PythonParser } from './PythonProvider';
import { RustParser } from './RustProvider';
import { TypescriptParser } from './TypescriptProvider';

export class FunctionParserLoader {
Expand All @@ -33,6 +34,7 @@ export class FunctionParserLoader {
java: JavaParser,
python: PythonParser,
php: PhpParser,
rust: RustParser,
};

public async loadParser(languageId: string): Promise<FunctionParamsParser | null> {
Expand Down
196 changes: 196 additions & 0 deletions src/function-params-parser/RustProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
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 './rust-splitParams';
import { FunctionParamsInfo, ParamsInfo, ReturnInfo } from './types';

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

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

const match = functionPattern.exec(functionDefinition);
if (match) {
matched = true;
const returnTypeStr = match[4] ? match[4].trim() : defaultReturnType;

returnType[defaultReturnName] = {
type: returnTypeStr,
description: '',
};

params = splitParams(functionParamsStr, languageSettings);
}

return { matched, returnType, params };
}

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

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

const match = functionPattern.exec(functionDefinition);

if (match) {
matched = true;
const returnTypeStr = match[5] ? match[5].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, matchAssociatedFunction];

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 RustParser 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),
};
}
}
45 changes: 45 additions & 0 deletions src/function-params-parser/rust-splitParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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 = /^(&self|&mut\s+self)?(\w+)\s*:\s*(.*)$/;
const match = paramPattern.exec(paramStr);
if (match) {
const name =
match[2] ||
(defaultCount > 0 ? `${defaultReturnName}${defaultCount++}` : defaultReturnName);
const type = match[3]?.trim() || defaultParamType;

params[name] = { type, description: '' };
}
paramStartIndex = i + 1;
}
}

return params;
}
17 changes: 17 additions & 0 deletions src/test/suite/rust-functionComment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { functionCommentTester } from './common/functionCommentTester';
import { TestInfo } from './types';

const testInfo: TestInfo = [
{
testName: 'rust-function',
workspaceName: 'function-comment-for-rust',
files: [
{ fileName: 'function-none-params-with-return.rs', cursorLine: 0 },
{ fileName: 'function-none-params-without-return.rs', cursorLine: 0 },
{ fileName: 'function-with-params-with-return.rs', cursorLine: 0 },
{ fileName: 'function-with-params-without-return.rs', cursorLine: 0 },
],
},
];

functionCommentTester(testInfo);

0 comments on commit c0c2ddb

Please sign in to comment.