diff options
| author | Rowan Goemans <RB.Goemans@student.han.nl> | 2017-12-31 02:03:08 +0100 |
|---|---|---|
| committer | Christoph Schlosser <christophschlosser@users.noreply.github.com> | 2018-02-20 22:02:22 +0100 |
| commit | 1746f21bc3720585d2fe877db8d69aae301ff02c (patch) | |
| tree | 7f0d081fd8fc3e0b54c7bb478cb8724a4cf3b798 | |
| parent | b446b9e596fc0487b88597d8e818dae61512f309 (diff) | |
| download | doxdocgen-1746f21bc3720585d2fe877db8d69aae301ff02c.tar.gz | |
-- Added many unit tests for the C parser. Almost complete now
-- Refactored project structure to allow for more overview once we add more languages.
-- Renamed a lot of types in the CParser to be more explicit.
-- Made configuration injectable into the classes that need them. This allows to unit test configuration variables.
-- Added 2 new config params that allows to customize what gets returned for a bool return type.
-- Small changes to the logic to make it more concise.
| -rw-r--r-- | package.json | 10 | ||||
| -rw-r--r-- | src/CodeParser/CParser/Argument.ts | 6 | ||||
| -rw-r--r-- | src/CodeParser/CParser/CppParser.ts | 16 | ||||
| -rw-r--r-- | src/CodeParser/CParser/ParseTree.ts | 121 | ||||
| -rw-r--r-- | src/CodeParserController.ts (renamed from src/CodeParser/CodeParserController.ts) | 27 | ||||
| -rw-r--r-- | src/Common/ICodeParser.ts (renamed from src/CodeParser/CodeParser.ts) | 6 | ||||
| -rw-r--r-- | src/Common/IDocGen.ts (renamed from src/DocGen/DocGen.ts) | 0 | ||||
| -rw-r--r-- | src/Config.ts | 57 | ||||
| -rw-r--r-- | src/DocGen/CGen.ts | 185 | ||||
| -rw-r--r-- | src/DocGen/CppGen.ts | 6 | ||||
| -rw-r--r-- | src/Lang/C/CArgument.ts | 6 | ||||
| -rw-r--r-- | src/Lang/C/CDocGen.ts | 183 | ||||
| -rw-r--r-- | src/Lang/C/CParseTree.ts | 121 | ||||
| -rw-r--r-- | src/Lang/C/CParser.ts (renamed from src/CodeParser/CParser/CParser.ts) | 293 | ||||
| -rw-r--r-- | src/Lang/C/CToken.ts (renamed from src/CodeParser/CParser/Token.ts) | 15 | ||||
| -rw-r--r-- | src/extension.ts | 2 | ||||
| -rw-r--r-- | src/test/CTests/Attributes.test.ts (renamed from src/test/Attributes.test.ts) | 15 | ||||
| -rw-r--r-- | src/test/CTests/FunctionPointer.test.ts (renamed from src/test/FunctionPointer.test.ts) | 10 | ||||
| -rw-r--r-- | src/test/CTests/Operators.test.ts | 256 | ||||
| -rw-r--r-- | src/test/CTests/ReturnTypes.test.ts (renamed from src/test/ReturnTypes.test.ts) | 14 | ||||
| -rw-r--r-- | src/test/CTests/Templates.test.ts (renamed from src/test/Templates.test.ts) | 17 | ||||
| -rw-r--r-- | src/test/CTests/TestSetup.ts (renamed from src/test/tools/TestSetup.ts) | 24 | ||||
| -rw-r--r-- | src/test/CTests/TrailingReturns.test.ts (renamed from src/test/TrailingReturns.test.ts) | 2 | ||||
| -rw-r--r-- | src/test/Operators.test.ts | 38 | ||||
| -rw-r--r-- | src/test/Variadic.test.ts | 38 |
25 files changed, 819 insertions, 649 deletions
diff --git a/package.json b/package.json index 041abf3..67b83b8 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,16 @@ "type": "boolean", "default": true }, + "doxdocgen.generic.boolReturnsTrueFalse": { + "description": "If this is enabled a bool return value will be split into true and false return param.", + "type": "boolean", + "default": true + }, + "doxdocgen.generic.boolPointerReturnsNull": { + "description": "If this is enabled a pointer to a bool return value will include a null return param.", + "type": "boolean", + "default": true + }, "doxdocgen.generic.briefTemplate": { "description": "The template of the brief DoxyGen line that is generated. If empty it won't get generated at all.", "type": "string", diff --git a/src/CodeParser/CParser/Argument.ts b/src/CodeParser/CParser/Argument.ts deleted file mode 100644 index 4cd793d..0000000 --- a/src/CodeParser/CParser/Argument.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ParseTree } from "./ParseTree"; - -export class Argument { - public Name: string = undefined; - public Type: ParseTree = new ParseTree(); -} diff --git a/src/CodeParser/CParser/CppParser.ts b/src/CodeParser/CParser/CppParser.ts deleted file mode 100644 index b3279c6..0000000 --- a/src/CodeParser/CParser/CppParser.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Position, TextDocumentContentChangeEvent, TextEditor, TextLine } from "vscode"; -import Generator from "../../DocGen/CGen"; -import { IDocGen } from "../../DocGen/DocGen"; -import CParser from "./CParser"; - -/** - * - * Parses C++ code for methods and signatures - * - * @export - * @class CppParser - * @implements {ICodeParser} - */ -export default class CppParser extends CParser { - // For now C++ is the same as C -} diff --git a/src/CodeParser/CParser/ParseTree.ts b/src/CodeParser/CParser/ParseTree.ts deleted file mode 100644 index bec5b91..0000000 --- a/src/CodeParser/CParser/ParseTree.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Token, TokenType } from "./Token"; - -export class ParseTree { - - /** - * Create a tree from tokens. This consumes the tokens. - * @param tokens The tokens to create a tree for. - * @param inNested If currently allready nesting. - */ - public static CreateTree(tokens: Token[], inNested: boolean = false): ParseTree { - const tree: ParseTree = new ParseTree(); - - while (tokens.length > 0) { - const token: Token = tokens.shift(); - switch (token.Type) { - case TokenType.OpenParenthesis: - tree.nodes.push(this.CreateTree(tokens, true)); - break; - case TokenType.CloseParenthesis: - if (inNested === false) { - throw new Error("Unmatched closing parenthesis."); - } - return tree; - default: - tree.nodes.push(token); - break; - } - } - - if (inNested === true) { - throw new Error("No match found for an opening parenthesis."); - } - - return tree; - } - - public nodes: Array<Token | ParseTree> = []; - - /** - * Compact empty branches. Example ((foo))(((bar))) will become (foo)(bar) - * @param tree The ParseTree to compact. Defaults to the current tree. - */ - public Compact(tree: ParseTree = this): ParseTree { - const newTree: ParseTree = new ParseTree(); - newTree.nodes = tree.nodes.map((n) => n); - const isNotCompact = (n) => n instanceof ParseTree && n.nodes.length === 1 && n.nodes[0] instanceof ParseTree; - - // Compact current level of nodes to the maximum amount. - while (newTree.nodes.some((n) => isNotCompact(n))) { - newTree.nodes = newTree.nodes - .map((n) => n instanceof ParseTree && isNotCompact(n) ? n.nodes[0] : n); - } - - // Compact all nested parsetrees. - newTree.nodes = newTree.nodes - .map((n) => n instanceof ParseTree ? this.Compact(n) : n); - - return newTree; - } - - /** - * Copy parsetree. - * @param tree The ParseTree to compact. Defaults to the current tree. - */ - public Copy(tree: ParseTree = this): ParseTree { - const newTree: ParseTree = new ParseTree(); - newTree.nodes = tree.nodes - .map((n) => n instanceof Token ? n : this.Copy(n)); - return newTree; - } - - /** - * Create string from the parsetree which is a representation of the original code. - * @param tree The ParseTree to compact. Defaults to the current tree. - */ - public Yield(tree: ParseTree = this): string { - let code: string = ""; - - for (const node of tree.nodes) { - if (node instanceof ParseTree) { - code += "(" + this.Yield(node) + ")"; - continue; - } - - switch (node.Type) { - case TokenType.Symbol: - code += code === "" ? node.Value : " " + node.Value; - break; - case TokenType.Pointer: - code += node.Value; - break; - case TokenType.Reference: - code += node.Value; - break; - case TokenType.ArraySubscript: - code += node.Value; - break; - case TokenType.CurlyBlock: - code += node.Value; - break; - case TokenType.Assignment: - code += " " + node.Value; - break; - case TokenType.Comma: - code += node.Value; - break; - case TokenType.Arrow: - code += " " + node.Value; - break; - case TokenType.Ellipsis: - code += node.Value; - break; - case TokenType.Attribute: - code += code === "" ? node.Value : " " + node.Value; - break; - } - } - - return code; - } -} diff --git a/src/CodeParser/CodeParserController.ts b/src/CodeParserController.ts index 7eae9c0..32d1d75 100644 --- a/src/CodeParser/CodeParserController.ts +++ b/src/CodeParserController.ts @@ -8,11 +8,9 @@ import { window, workspace, } from "vscode"; -import { Config, ConfigType } from "../Config"; -import CodeParser from "./CodeParser"; -import CParser from "./CParser/CParser"; -import CppParser from "./CParser/CppParser"; - +import CodeParser from "./Common/ICodeParser"; +import { Config } from "./Config"; +import CParser from "./Lang/C/CParser"; /** * * Checks if the event matches the specified guidelines and if a parser exists for this language @@ -22,7 +20,7 @@ import CppParser from "./CParser/CppParser"; */ export default class CodeParserController { private disposable: Disposable; - private triggerSequence: string; + private cfg: Config; /** * Creates an instance of CodeParserController @@ -36,8 +34,7 @@ export default class CodeParserController { workspace.onDidChangeTextDocument((event) => { const activeEditor: TextEditor = window.activeTextEditor; if (activeEditor && event.document === activeEditor.document) { - this.readConfig(); - + this.cfg = Config.ImportFromSettings(); this.onEvent(activeEditor, event.contentChanges[0]); } }, this, subscriptions); @@ -59,12 +56,6 @@ export default class CodeParserController { Implementation ***************************************************************************/ - private readConfig() { - this.triggerSequence = workspace - .getConfiguration(ConfigType.generic) - .get<string>(Config.triggerSequence, "/**"); - } - private check(activeEditor: TextEditor, event: TextDocumentContentChangeEvent): boolean { if (activeEditor == null || event.text == null) { return false; @@ -81,7 +72,7 @@ export default class CodeParserController { const cont: string = activeLine.text.trim(); - return this.triggerSequence === cont; + return this.cfg.triggerSequence === cont; } private onEvent(activeEditor: TextEditor, event: TextDocumentContentChangeEvent) { @@ -94,10 +85,8 @@ export default class CodeParserController { switch (lang) { case "c": - parser = new CParser(); - break; case "cpp": - parser = new CppParser(); + parser = new CParser(this.cfg); break; default: // tslint:disable-next-line:no-console @@ -108,7 +97,7 @@ export default class CodeParserController { const currentPos: Position = window.activeTextEditor.selection.active; const startReplace: Position = new Position( currentPos.line, - currentPos.character - this.triggerSequence.length, + currentPos.character - this.cfg.triggerSequence.length, ); const nextLineText: string = window.activeTextEditor.document.lineAt(startReplace.line + 1).text; diff --git a/src/CodeParser/CodeParser.ts b/src/Common/ICodeParser.ts index cc2c709..afe2967 100644 --- a/src/CodeParser/CodeParser.ts +++ b/src/Common/ICodeParser.ts @@ -1,9 +1,9 @@ -import { Position, TextDocumentContentChangeEvent, TextEditor } from "vscode"; +import { TextEditor } from "vscode"; +import { IDocGen } from "./IDocGen"; export default interface ICodeParser { - /** * @param {TextEditor} activeEditor The open active Editor where the event came from */ - Parse(activeEditor: TextEditor); + Parse(activeEditor: TextEditor): IDocGen; } diff --git a/src/DocGen/DocGen.ts b/src/Common/IDocGen.ts index 1b5b1e1..1b5b1e1 100644 --- a/src/DocGen/DocGen.ts +++ b/src/Common/IDocGen.ts diff --git a/src/Config.ts b/src/Config.ts index 3cc78fb..6297a66 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -1,18 +1,43 @@ -export enum ConfigType { - generic = "doxdocgen.generic", -} +import { workspace } from "vscode"; + +export class Config { + public static ImportFromSettings(): Config { + const values: Config = new Config(); + + const cfg = workspace.getConfiguration("doxdocgen.generic"); + + values.firstLine = cfg.get<string>("firstLine", values.firstLine); + values.commentPrefix = cfg.get<string>("commentPrefix", values.commentPrefix); + values.lastLine = cfg.get<string>("lastLine", values.lastLine); + values.newLineAfterBrief = cfg.get<boolean>("newLineAfterBrief", values.newLineAfterBrief); + values.newLineAfterParams = cfg.get<boolean>("newLineAfterParams", values.newLineAfterParams); + values.newLineAfterTParams = cfg.get<boolean>("newLineAfterTParams", values.newLineAfterTParams); + values.includeTypeAtReturn = cfg.get<boolean>("includeTypeAtReturn", values.includeTypeAtReturn); + values.boolReturnsTrueFalse = cfg.get<boolean>("boolReturnsTrueFalse", values.boolReturnsTrueFalse); + values.boolPointerReturnsNull = cfg.get<boolean>("boolPointerReturnsNull", values.boolPointerReturnsNull); + values.briefTemplate = cfg.get<string>("briefTemplate", values.briefTemplate); + values.paramTemplate = cfg.get<string>("paramTemplate", values.paramTemplate); + values.tparamTemplate = cfg.get<string>("tparamTemplate", values.tparamTemplate); + values.returnTemplate = cfg.get<string>("returnTemplate", values.returnTemplate); + + return values; + } + + public readonly paramTemplateReplace: string = "{param}"; + public readonly typeTemplateReplace: string = "{type}"; -export enum Config { - triggerSequence = "triggerSequence", - firstLine = "firstLine", - commentPrefix = "commentPrefix", - lastLine = "lastLine", - newLineAfterBrief = "newLineAfterBrief", - newLineAfterParams = "newLineAfterParams", - newLineAfterTParams = "newLineAfterTParams", - includeTypeAtReturn = "includeTypeAtReturn", - briefTemplate = "briefTemplate", - paramTemplate = "paramTemplate", - tparamTemplate = "tparamTemplate", - returnTemplate = "returnTemplate", + public triggerSequence: string = "/**"; + public firstLine: string = "/**"; + public commentPrefix: string = " * "; + public lastLine: string = " */"; + public newLineAfterBrief: boolean = true; + public newLineAfterParams: boolean = false; + public newLineAfterTParams: boolean = false; + public includeTypeAtReturn: boolean = true; + public boolReturnsTrueFalse: boolean = true; + public boolPointerReturnsNull: boolean = true; + public briefTemplate: string = "@brief "; + public paramTemplate: string = "@param {param} "; + public tparamTemplate: string = "@tparam {param} "; + public returnTemplate: string = "@return {type} "; } diff --git a/src/DocGen/CGen.ts b/src/DocGen/CGen.ts deleted file mode 100644 index 13ecbf1..0000000 --- a/src/DocGen/CGen.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { Position, Range, Selection, TextEditor, TextLine, workspace, WorkspaceEdit } from "vscode"; -import { Config, ConfigType } from "../Config"; -import { IDocGen } from "./DocGen"; - -export default class CGen implements IDocGen { - protected firstLine: string; - protected commentPrefix: string; - protected lastLine: string; - protected newLineAfterBrief: boolean; - protected newLineAfterParams: boolean; - protected newLineAfterTParams: boolean; - protected includeTypeAtReturn: boolean; - protected briefTemplate: string; - protected paramTemplate: string; - protected tparamTemplate: string; - protected returnTemplate: string; - - protected templateParamReplace: string; - protected templateTypeReplace: string; - - protected activeEditor: TextEditor; - - protected retVals: string[]; - protected params: string[]; - protected tparams: string[]; - - /** - * @param {TextEditor} actEdit Active editor window - * @param {Position} cursorPosition Where the cursor of the user currently is - * @param {string[]} params The parameter names of the method extracted by the parser - * @param {string[]} tparam The template parameter names of the method extracted by the parser. - * @param {string[]} returnVals The return values extracted by the parser - */ - public constructor( - actEdit: TextEditor, - cursorPosition: Position, - params: string[], - tparam: string[], - returnVals: string[], - ) { - this.activeEditor = actEdit; - this.templateParamReplace = "{param}"; - this.templateTypeReplace = "{type}"; - this.params = params; - this.tparams = tparam; - this.retVals = returnVals; - } - - /** - * @inheritdoc - */ - public GenerateDoc(rangeToReplace: Range) { - this.readConfig(); - const comment: string = this.generateComment(); - - this.activeEditor.edit((editBuilder) => { - editBuilder.replace(rangeToReplace, comment); // Insert the comment - }); - - // Set cursor to first DoxyGen command. - this.moveCursurToFirstDoxyCommand(comment, rangeToReplace.start.line, rangeToReplace.start.character); - } - - /*************************************************************************** - Implementation - ***************************************************************************/ - - protected readConfig() { - const getCfg = workspace.getConfiguration; - - this.firstLine = getCfg(ConfigType.generic).get<string>(Config.firstLine, "/**"); - this.commentPrefix = getCfg(ConfigType.generic).get<string>(Config.commentPrefix, " * "); - this.lastLine = getCfg(ConfigType.generic).get<string>(Config.lastLine, " */"); - this.newLineAfterBrief = getCfg(ConfigType.generic).get<boolean>(Config.newLineAfterBrief, true); - this.newLineAfterParams = getCfg(ConfigType.generic).get<boolean>(Config.newLineAfterParams, false); - this.newLineAfterTParams = getCfg(ConfigType.generic).get<boolean>(Config.newLineAfterTParams, false); - this.includeTypeAtReturn = getCfg(ConfigType.generic).get<boolean>(Config.includeTypeAtReturn, false); - this.briefTemplate = getCfg(ConfigType.generic).get<string>(Config.briefTemplate, "@brief "); - this.paramTemplate = getCfg(ConfigType.generic).get<string>(Config.paramTemplate, "@param {param} "); - this.tparamTemplate = getCfg(ConfigType.generic).get<string>(Config.tparamTemplate, "@tparam {param} "); - this.returnTemplate = getCfg(ConfigType.generic).get<string>(Config.returnTemplate, "@return {type} "); - } - - protected getIndentation(): string { - const line: TextLine = this.activeEditor.document.lineAt(this.activeEditor.selection.start.line); - const lineTxt: string = line.text; - let stringToIndent: string = ""; - // Find indentation from previous line - for (let i = 0; i < line.firstNonWhitespaceCharacterIndex; i++) { - if (lineTxt.charAt(i) === "\t") { - stringToIndent = stringToIndent + "\t"; - } else if (lineTxt.charAt(i) === " ") { - stringToIndent = stringToIndent + " "; - } - } - return stringToIndent; - } - - protected getTemplatedString(replace: string, template: string, param: string): string { - return template.replace(replace, param); - } - - protected generateBrief(lines: string[]) { - lines.push(this.commentPrefix + this.briefTemplate); - } - - protected generateFromTemplate(lines: string[], replace: string, template: string, templateWith: string[]) { - let line: string = ""; - - templateWith.forEach((element: string) => { - // Ignore null values - if (element !== null) { - line = this.commentPrefix; - line += this.getTemplatedString(replace, template, element); - lines.push(line); - } - }); - } - - protected generateComment(): string { - const lines: string[] = []; - - if (this.firstLine.trim().length !== 0) { - lines.push(this.firstLine); - } - - if (this.briefTemplate.trim().length !== 0) { - this.generateBrief(lines); - if (this.newLineAfterBrief === true) { - lines.push(this.commentPrefix); - } - } - - if (this.tparamTemplate.trim().length !== 0 && this.tparams.length > 0) { - this.generateFromTemplate(lines, this.templateParamReplace, this.tparamTemplate, this.tparams); - if (this.newLineAfterTParams === true) { - lines.push(this.commentPrefix); - } - } - - if (this.paramTemplate.trim().length !== 0 && this.params.length > 0) { - this.generateFromTemplate(lines, this.templateParamReplace, this.paramTemplate, this.params); - if (this.newLineAfterParams === true) { - lines.push(this.commentPrefix); - } - } - - if (this.returnTemplate.trim().length !== 0 && this.retVals.length > 0) { - if (this.includeTypeAtReturn === false) { - this.retVals = this.retVals.map((t) => t === "true" || t === "false" || t === "null" ? t : ""); - } - - this.generateFromTemplate(lines, this.templateTypeReplace, this.returnTemplate, this.retVals); - } - - if (this.lastLine.trim().length !== 0) { - lines.push(this.lastLine); - } - - const comment: string = lines.join("\n" + this.getIndentation()); - return comment; - } - - protected moveCursurToFirstDoxyCommand(comment: string, baseLine: number, baseCharacter) { - // Find first offset of a new line in the comment. Since that's when the line where the first param starts. - let line: number = baseLine; - let character: number = comment.indexOf("\n"); - - // If a first line is included find the 2nd line with a newline. - if (this.firstLine.trim().length !== 0) { - line++; - const oldCharacter: number = character; - character = comment.indexOf("\n", oldCharacter + 1) - oldCharacter; - } - - // If newline is not found means no first param was found so Set to base line before the newline. - if (character < 0) { - line = baseLine; - character = baseCharacter; - } - - const moveTo: Position = new Position(line, character); - this.activeEditor.selection = new Selection(moveTo, moveTo); - } -} diff --git a/src/DocGen/CppGen.ts b/src/DocGen/CppGen.ts deleted file mode 100644 index c13d194..0000000 --- a/src/DocGen/CppGen.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Position, Range, Selection, TextEditor, TextLine, WorkspaceEdit } from "vscode"; -import CGen from "./CGen"; - -export default class CppGen extends CGen { - // For now C++ is the same as C -} diff --git a/src/Lang/C/CArgument.ts b/src/Lang/C/CArgument.ts new file mode 100644 index 0000000..46f180c --- /dev/null +++ b/src/Lang/C/CArgument.ts @@ -0,0 +1,6 @@ +import { CParseTree } from "./CParseTree"; + +export class CArgument { + public name: string = null; + public type: CParseTree = new CParseTree(); +} diff --git a/src/Lang/C/CDocGen.ts b/src/Lang/C/CDocGen.ts new file mode 100644 index 0000000..87d59fb --- /dev/null +++ b/src/Lang/C/CDocGen.ts @@ -0,0 +1,183 @@ +import { Position, Range, Selection, TextEditor, TextLine, WorkspaceEdit } from "vscode"; +import { IDocGen } from "../../Common/IDocGen"; +import { Config } from "../../Config"; +import { CArgument } from "./CArgument"; +import { CParseTree } from "./CParseTree"; +import { CToken, CTokenType } from "./CToken"; + +export default class CDocGen implements IDocGen { + protected activeEditor: TextEditor; + + protected readonly cfg: Config; + + protected func: CArgument; + protected templateParams: string[]; + protected params: CArgument[]; + + /** + * @param {TextEditor} actEdit Active editor window + * @param {Position} cursorPosition Where the cursor of the user currently is + * @param {string[]} templateParams The template parameters of the declaration. + * @param {CArgument} func The type and name of the function to generate doxygen. + * Doesn't contain anything if it is not a function. + * @param {CArgument[]} params The parameters of the function. Doesn't contain anything if it is not a function. + */ + public constructor( + actEdit: TextEditor, + cursorPosition: Position, + cfg: Config, + templateParams: string[], + func: CArgument, + params: CArgument[], + ) { + this.activeEditor = actEdit; + this.cfg = cfg; + this.func = func; + this.templateParams = templateParams; + this.params = params; + } + + /** + * @inheritdoc + */ + public GenerateDoc(rangeToReplace: Range) { + const comment: string = this.generateComment(); + + this.activeEditor.edit((editBuilder) => { + editBuilder.replace(rangeToReplace, comment); // Insert the comment + }); + + // Set cursor to first DoxyGen command. + this.moveCursurToFirstDoxyCommand(comment, rangeToReplace.start.line, rangeToReplace.start.character); + } + + /*************************************************************************** + Implementation + ***************************************************************************/ + protected getIndentation(): string { + const line: TextLine = this.activeEditor.document.lineAt(this.activeEditor.selection.start.line); + return line.text.slice(0, line.firstNonWhitespaceCharacterIndex); + } + + protected getTemplatedString(replace: string, template: string, param: string): string { + return template.replace(replace, param); + } + + protected generateBrief(lines: string[]) { + lines.push(this.cfg.commentPrefix + this.cfg.briefTemplate); + } + + protected generateFromTemplate(lines: string[], replace: string, template: string, templateWith: string[]) { + let line: string = ""; + + templateWith.forEach((element: string) => { + // Ignore null values + if (element !== null && element !== undefined && element !== "") { + line = this.cfg.commentPrefix; + line += this.getTemplatedString(replace, template, element); + lines.push(line); + } + }); + } + + protected generateReturnParams(): string[] { + const params: string[] = []; + + // Check if return type is a pointer + const ptrReturnIndex = this.func.type.nodes + .findIndex((n) => n instanceof CToken && n.type === CTokenType.Pointer); + + // Special case for void functions. + const voidReturnIndex = this.func.type.nodes + .findIndex((n) => n instanceof CToken && n.type === CTokenType.Symbol && n.value === "void"); + + // Special case for bool return type. + const boolReturnIndex: number = this.func.type.nodes + .findIndex((n) => n instanceof CToken && n.type === CTokenType.Symbol && n.value === "bool"); + + if (boolReturnIndex !== -1) { + if (this.cfg.boolReturnsTrueFalse === true) { + params.push("true"); + params.push("false"); + } + if (ptrReturnIndex !== -1 && this.cfg.boolPointerReturnsNull === true) { + params.push("null"); + } + } else if (voidReturnIndex !== -1 && ptrReturnIndex !== -1) { + params.push(this.cfg.includeTypeAtReturn === true ? this.func.type.Yield() : ""); + } else if (voidReturnIndex === -1 && this.func.type.nodes.length > 0) { + params.push(this.cfg.includeTypeAtReturn === true ? this.func.type.Yield() : ""); + } + + return params; + } + + protected generateComment(): string { + const lines: string[] = []; + + if (this.cfg.firstLine.trim().length !== 0) { + lines.push(this.cfg.firstLine); + } + + if (this.cfg.briefTemplate.trim().length !== 0) { + this.generateBrief(lines); + if (this.cfg.newLineAfterBrief === true) { + lines.push(this.cfg.commentPrefix); + } + } + + if (this.cfg.tparamTemplate.trim().length !== 0 && this.templateParams.length > 0) { + this.generateFromTemplate( + lines, + this.cfg.paramTemplateReplace, + this.cfg.paramTemplate, + this.templateParams, + ); + if (this.cfg.newLineAfterTParams === true) { + lines.push(this.cfg.commentPrefix); + } + } + + if (this.cfg.paramTemplate.trim().length !== 0 && this.params.length > 0) { + const paramNames: string[] = this.params.map((p) => p.name); + this.generateFromTemplate(lines, this.cfg.paramTemplateReplace, this.cfg.paramTemplate, paramNames); + if (this.cfg.newLineAfterParams === true) { + lines.push(this.cfg.commentPrefix); + } + } + + if (this.cfg.returnTemplate.trim().length !== 0 && this.func.type !== null) { + const returnParams = this.generateReturnParams(); + this.generateFromTemplate(lines, this.cfg.typeTemplateReplace, this.cfg.returnTemplate, returnParams); + } + + if (this.cfg.lastLine.trim().length !== 0) { + lines.push(this.cfg.lastLine); + } + + const comment: string = lines.join("\n" + this.getIndentation()); + return comment; + } + + protected moveCursurToFirstDoxyCommand(comment: string, baseLine: number, baseCharacter) { + // Find first offset of a new line in the comment. Since that's when the line where the first param starts. + let line: number = baseLine; + let character: number = comment.indexOf("\n"); + + // If a first line is included find the 2nd line with a newline. + if (this.cfg.firstLine.trim().length !== 0) { + line++; + const oldCharacter: number = character; + character = comment.indexOf("\n", oldCharacter + 1) - oldCharacter; + } + + // If newline is not found means no first param was found so Set to base line before the newline. + if (character < 0) { + line = baseLine; + character = baseCharacter; + } |