diff options
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | src/CodeParserController.ts | 17 | ||||
| -rw-r--r-- | src/Lang/Cpp/CppDocGen.ts | 134 | ||||
| -rw-r--r-- | src/templatedString.ts | 84 | ||||
| -rw-r--r-- | src/test/CppTests/Config.test.ts | 8 | ||||
| -rw-r--r-- | src/test/CppTests/FileDescription.test.ts | 4 | ||||
| -rw-r--r-- | src/util.ts | 54 |
8 files changed, 182 insertions, 127 deletions
@@ -148,7 +148,7 @@ Each of them can be configured with its own custom text and you can decide if th "@copyright Copyright (c) {year}" ], - // Additional file documentation. Array of strings will be converted to one line per element. Can template {year}, {date}, {author}, and {email}. + // Additional file documentation. One tag per line will be added. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix. "doxdocgen.file.customTag": [], // The order to use for the file comment. Values can be used multiple times. Valid values are shown in default setting. @@ -210,7 +210,7 @@ Each of them can be configured with its own custom text and you can decide if th "custom" ], - // Custom tags to be added to the generic order. One tag per line will be added. You have to specify the prefix yourself. + // Custom tags to be added to the generic order. One tag per line will be added. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix. "doxdocgen.generic.customTags": [], // The template of the param Doxygen line(s) that are generated. If empty it won't get generated at all. diff --git a/package.json b/package.json index c065138..a0bf1d3 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "default": "@version 0.1" }, "doxdocgen.file.customTag": { - "markdownDescription": "Additional file documentation. Array of strings will be converted to one line per element. Can template `{year}`, `{date}`, `{author}` and `{email}`.", + "markdownDescription": "Additional file documentation. Array of strings will be converted to one line per element. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix.", "type": [ "array", "string" @@ -214,7 +214,7 @@ ] }, "doxdocgen.generic.customTags": { - "description": "Custom tags to be added to the generic order. One tag per line will be added. You have to specify the prefix yourself.", + "description": "Custom tags to be added to the generic order. One tag per line will be added. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix.", "type": [ "array", "string" diff --git a/src/CodeParserController.ts b/src/CodeParserController.ts index f87e221..3b6348a 100644 --- a/src/CodeParserController.ts +++ b/src/CodeParserController.ts @@ -12,6 +12,7 @@ import CodeParser from "./Common/ICodeParser"; import { Config } from "./Config"; import GitConfig from "./GitConfig"; import CppParser from "./Lang/Cpp/CppParser"; +import { inComment } from "./util"; /** * * Checks if the event matches the specified guidelines and if a parser exists for this language @@ -75,7 +76,7 @@ export default class CodeParserController { } // Check if currently in a comment block - if (this.inComment(activeEditor, activeSelection.line)) { + if (inComment(activeEditor, activeSelection.line)) { return false; } @@ -92,20 +93,6 @@ export default class CodeParserController { } } - private inComment(activeEditor: TextEditor, activeLine: number): boolean { - if (activeLine === 0) { - return false; - } - - const txt: string = activeEditor.document.lineAt(activeLine - 1).text.trim(); - if (!txt.startsWith("///") && !txt.startsWith("*") && - !txt.startsWith("/**") && !txt.startsWith("/*!")) { - return false; - } else { - return true; - } - } - private onEvent(activeEditor: TextEditor, event: TextDocumentContentChangeEvent) { if (!this.check(activeEditor, event)) { return null; diff --git a/src/Lang/Cpp/CppDocGen.ts b/src/Lang/Cpp/CppDocGen.ts index 05c8883..c0113ad 100644 --- a/src/Lang/Cpp/CppDocGen.ts +++ b/src/Lang/Cpp/CppDocGen.ts @@ -1,9 +1,10 @@ -import * as env from "env-var"; import * as moment from "moment"; import { Position, Range, Selection, TextEditor } from "vscode"; import { IDocGen } from "../../Common/IDocGen"; import { Config } from "../../Config"; import GitConfig from "../../GitConfig"; +import * as templates from "../../templatedString"; +import { getIndentation } from "../../util"; import { CppArgument } from "./CppArgument"; import * as CppParser from "./CppParser"; import { CppToken, CppTokenType } from "./CppToken"; @@ -111,79 +112,16 @@ export class CppDocGen implements IDocGen { }); // Set cursor to first DoxyGen command. - this.moveCursurToFirstDoxyCommand(comment, - modifiedRangeToReplace.start.line, - modifiedRangeToReplace.start.character); + this.moveCursurToFirstDoxyCommand( + comment, + modifiedRangeToReplace.start.line, + modifiedRangeToReplace.start.character, + ); } /*************************************************************************** Implementation ***************************************************************************/ - protected getIndentation(): string { - return this.activeEditor.document.lineAt(this.activeEditor.selection.start.line).text.match("^\\s*")[0]; - } - - protected getIndentedTemplate(replace: string): string { - if (replace === undefined || replace === null || replace === "") { - return ""; - } - const snippets = replace.split(/({indent:\d+})/); - - let indentedString: string = ""; - let indentWidth: number = 0; - - // tslint:disable-next-line:prefer-for-of - snippets.forEach((element) => { - if (element.match(/{indent:\d+}/)) { - const indents = parseInt(element.match(/{indent:(\d+)}/)[1], 10); - indentWidth = indents; - const numSpaces = Math.max(indentWidth - indentedString.length, 0); - indentedString += " ".repeat(numSpaces); - } else { - // just some text - indentedString += element; - } - }); - - return indentedString; - } - - protected getEnvVars(replace: string): string { - let replacement = replace; - const regex = /\$\{env\:([\w|\d|_]+)\}/m; - let match: RegExpExecArray; - - // tslint:disable-next-line:no-conditional-assignment - while ((match = regex.exec(replacement)) !== null) { - if (match.index === regex.lastIndex) { - regex.lastIndex++; - } - - const m = match[1]; - - const envVar: string = env.get(m, m).asString(); - - replacement = replacement.replace("${env:" + m + "}", envVar); - } - - return replacement; - } - - protected getTemplatedString(replace: string, template: string, param: string): string { - const replacedTemplate = template.replace(replace, param); - const replacedWithEnv = this.getEnvVars(replacedTemplate); - return this.getIndentedTemplate(replacedWithEnv); - } - - protected getMultiTemplatedString(replace: string[], template: string, param: string[]): string { - // For each replace entry, attempt to replace it with the corresponding param in the template - for (let i = 0; i < replace.length; i++) { - if (i < param.length) { - template = template.replace(replace[i], param[i]); - } - } - return this.getEnvVars(template); - } protected getSmartText(): string { if (!this.cfg.Generic.generateSmartText) { @@ -234,35 +172,20 @@ export class CppDocGen implements IDocGen { return ""; } } - const str = this.getTemplatedString(this.cfg.nameTemplateReplace, - text, - val); + const str = templates.getTemplatedString(text, { toReplace: this.cfg.nameTemplateReplace, with: val }); this.smartTextLength = str.length; return str; } protected generateBrief(lines: string[]) { lines.push( - ...this.getTemplatedString( - this.cfg.textTemplateReplace, + ...templates.getTemplatedString( this.cfg.Generic.briefTemplate, - this.getSmartText(), + { toReplace: this.cfg.textTemplateReplace, with: this.getSmartText() }, ).split("\n"), ); } - protected generateFromTemplate(lines: string[], - replace: string, - template: string, - templateWith: string[]) { - templateWith.forEach((element: string) => { - // Ignore null values - if (element !== null) { - lines.push(...this.getTemplatedString(replace, template, element).split("\n")); - } - }); - } - protected generateReturnParams(): string[] { const params: string[] = []; @@ -303,10 +226,12 @@ export class CppDocGen implements IDocGen { const authorInfo = this.getAuthorInfo(); // Allow substitution of {author} and {email} only lines.push( - ...this.getMultiTemplatedString( - [this.cfg.authorTemplateReplace, this.cfg.emailTemplateReplace], + ...templates.getMultiTemplatedString( this.cfg.Generic.authorTag, - [authorInfo.authorName, authorInfo.authorEmail], + [ + { toReplace: this.cfg.authorTemplateReplace, with: authorInfo.authorName }, + { toReplace: this.cfg.emailTemplateReplace, with: authorInfo.authorEmail }, + ], ).split("\n"), ); } @@ -314,7 +239,7 @@ export class CppDocGen implements IDocGen { protected generateFilenameFromTemplate(lines: string[]) { if (this.cfg.File.fileTemplate.trim().length !== 0) { - this.generateFromTemplate( + templates.generateFromTemplate( lines, this.cfg.nameTemplateReplace, this.cfg.File.fileTemplate, @@ -332,7 +257,7 @@ export class CppDocGen implements IDocGen { protected generateCopyrightTag(lines: string[]) { // This currently only supports year substitution this.cfg.File.copyrightTag.forEach((element) => { - this.generateFromTemplate( + templates.generateFromTemplate( lines, this.cfg.yearTemplateReplace, element, @@ -392,14 +317,17 @@ export class CppDocGen implements IDocGen { if (element !== "custom") { // Prevent recursive expansion // Allow any of date, year, author, email to be replaced lines.push( - ...this.getMultiTemplatedString( - [this.cfg.authorTemplateReplace, this.cfg.emailTemplateReplace, - this.cfg.dateTemplateReplace, this.cfg.yearTemplateReplace], + ...templates.getMultiTemplatedString( element, - [authorInfo.authorName, authorInfo.authorEmail, - moment().format(dateFormat), moment().format("YYYY")], + [ + { toReplace: this.cfg.authorTemplateReplace, with: authorInfo.authorName }, + { toReplace: this.cfg.emailTemplateReplace, with: authorInfo.authorEmail }, + { toReplace: this.cfg.dateTemplateReplace, with: moment().format(dateFormat) }, + { toReplace: this.cfg.yearTemplateReplace, with: moment().format("YYYY") }, + { toReplace: "{file}", with: this.activeEditor.document.fileName.replace(/^.*[\\\/]/, "")}, + ], ).split("\n"), - ); // TODO: clean up this + ); } }); } @@ -407,7 +335,7 @@ export class CppDocGen implements IDocGen { protected generateDateFromTemplate(lines: string[]) { if (this.cfg.Generic.dateTemplate.trim().length !== 0 && this.cfg.Generic.dateFormat.trim().length !== 0) { - this.generateFromTemplate( + templates.generateFromTemplate( lines, this.cfg.dateTemplateReplace, this.cfg.Generic.dateTemplate, @@ -467,7 +395,7 @@ export class CppDocGen implements IDocGen { switch (element) { case "tparam": { if (this.cfg.Cpp.tparamTemplate.trim().length !== 0 && this.templateParams.length > 0) { - this.generateFromTemplate( + templates.generateFromTemplate( lines, this.cfg.paramTemplateReplace, this.cfg.Cpp.tparamTemplate, @@ -479,7 +407,7 @@ export class CppDocGen implements IDocGen { case "param": { if (this.cfg.Generic.paramTemplate.trim().length !== 0 && this.params.length > 0) { const paramNames: string[] = this.params.map((p) => p.name); - this.generateFromTemplate( + templates.generateFromTemplate( lines, this.cfg.paramTemplateReplace, this.cfg.Generic.paramTemplate, @@ -491,7 +419,7 @@ export class CppDocGen implements IDocGen { case "return": { if (this.cfg.Generic.returnTemplate.trim().length !== 0 && this.func.type !== null) { const returnParams = this.generateReturnParams(); - this.generateFromTemplate( + templates.generateFromTemplate( lines, this.cfg.typeTemplateReplace, this.cfg.Generic.returnTemplate, @@ -514,7 +442,7 @@ export class CppDocGen implements IDocGen { this.insertFirstLine(lines); this.insertLastLine(lines); - return lines.join(`\n${this.getIndentation()}`); + return lines.join(`\n${getIndentation(this.activeEditor)}`); } protected moveCursurToFirstDoxyCommand(comment: string, baseLine: number, baseCharacter) { diff --git a/src/templatedString.ts b/src/templatedString.ts new file mode 100644 index 0000000..c397ad9 --- /dev/null +++ b/src/templatedString.ts @@ -0,0 +1,84 @@ +import { getEnvVars } from "./util"; + +/** + * Represent a templated variable in string + */ +export interface ITemplate { + toReplace: string; // The template to be replaced in string + with: string; // The value to replace with +} + +export function getIndentedTemplate(replace: string): string { + if (replace === "") { + return ""; + } + const snippets = replace.split(/({indent:\d+})/); + + let indentedString: string = ""; + let indentWidth: number = 0; + + // tslint:disable-next-line:prefer-for-of + snippets.forEach((element) => { + if (element.match(/{indent:\d+}/)) { + const indents = parseInt(element.match(/{indent:(\d+)}/)[1], 10); + indentWidth = indents; + const numSpaces = Math.max(indentWidth - indentedString.length, 0); + indentedString += " ".repeat(numSpaces); + } else { + // just some text + indentedString += element; + } + }); + + return indentedString; +} + +/** + * Expand variable template in the string + * @param original the original string + * @param template variable template to be expanded + * @returns new string with expanded template + */ +export function getTemplatedString(original: string, template: ITemplate): string { + const replacedTemplate = original.replace(template.toReplace, template.with); + const replacedWithEnv = getEnvVars(replacedTemplate); + return getIndentedTemplate(replacedWithEnv); +} + +/** + * Generate lines of doxygen comments from template + * @param lines Arrays to store lines of comments + * @param replace Variable template to be expanded + * @param template Original string that contains variable templates + * @param templateWith Arrays of values to replace in the original template string + */ +export function generateFromTemplate( + lines: string[], + replace: string, + template: string, + templateWith: string[], +) { + templateWith.forEach((element: string) => { + // Ignore null values + if (element !== null) { + lines.push(...getTemplatedString(template, { toReplace: replace, with: element }).split("\n")); + } + }); +} + +/** + * Expand multiple variable templates in the string + * @param original string containing multiple variables to be expanded + * @param templates variable templates to be expanded + * @returns new string with expanded templates + */ +export function getMultiTemplatedString( + original: string, + templates: ITemplate[], +): string { + // For each replace entry, attempt to replace it with the corresponding param in the template + for (const template of templates) { + original = original.replace(template.toReplace, template.with); + } + return getEnvVars(original); +} diff --git a/src/test/CppTests/Config.test.ts b/src/test/CppTests/Config.test.ts index 1afbef3..f6ec573 100644 --- a/src/test/CppTests/Config.test.ts +++ b/src/test/CppTests/Config.test.ts @@ -11,6 +11,7 @@ import * as assert from "assert"; import * as moment from "moment"; import * as vscode from "vscode"; import { Config } from "../../Config"; +import GitConfig from "../../GitConfig"; import TestSetup from "./TestSetup"; // Defines a Mocha test suite to group tests of similar kind together @@ -242,11 +243,12 @@ suite("C++ - Configuration Tests", () => { "@author {author}", "@date {date}", "@note {email}", + "@file {file}", ]; - testSetup.cfg.Generic.useGitUserName = true; - testSetup.cfg.Generic.useGitUserEmail = true; const result = testSetup.SetLine("void foo();").GetResult(); - assert.notStrictEqual(result, "/**\n * @author {author}\n * @date {date}\n * @note {email}\n */"); + const date = moment().format("YYYY-MM-DD"); + assert.strictEqual(result, `/**\n * @author your name\n * @date ${date}\n * @note you@domain.com\n` + + ` * @file MockDocument.h\n */`); }); test("Env variable", () => { diff --git a/src/test/CppTests/FileDescription.test.ts b/src/test/CppTests/FileDescription.test.ts index 2e0bb7e..ec0034d 100644 --- a/src/test/CppTests/FileDescription.test.ts +++ b/src/test/CppTests/FileDescription.test.ts @@ -78,9 +78,9 @@ suite("File Description Tests", () => { test("custom block", () => { testSetup.cfg.File.fileOrder = ["custom"]; testSetup.cfg.File.customTag = ["First Line", "{year} Year Line", "{date} Date Line", - "{author} Author Line", "{email} Email Line"]; + "{author} Author Line", "{email} Email Line", "{file} File Line"]; const result = testSetup.SetLine("").GetResult(); assert.strictEqual(result, "/**\n * First Line\n * " + year + " Year Line\n * " + date + " Date Line\n" + - " * your name Author Line\n * you@domain.com Email Line\n */"); + " * your name Author Line\n * you@domain.com Email Line\n" + " * MockDocument.h File Line\n */"); }); }); diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..f2068e7 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,54 @@ +import * as env from "env-var"; +import * as vscode from "vscode"; + +/** + * Check if a specific line is inside a comment block + * @param activeEditor the active editor + * @param activeLine the line number to be checked + */ +export function inComment(activeEditor: vscode.TextEditor, activeLine: number): boolean { + if (activeLine === 0) { + return false; + } + + const txt: string = activeEditor.document.lineAt(activeLine - 1).text.trim(); + if (!txt.startsWith("///") && !txt.startsWith("*") && + !txt.startsWith("/**") && !txt.startsWith("/*!")) { + return false; + } else { + return true; + } +} + +/** + * Get the indentation string for the current line (line at the current cursor position) + */ +export function getIndentation(editor: vscode.TextEditor = vscode.window.activeTextEditor): string { + return editor.document.lineAt(editor.selection.start.line).text.match("^\\s*")[0]; +} + +/** + * Expand enviroment variables in the string + * @param replace string containing environment variables + * @returns new string with expanded enviroment variables + */ +export function getEnvVars(replace: string): string { + let replacement = replace; + const regex = /\$\{env\:([\w|\d|_]+)\}/m; + let match: RegExpExecArray; + + // tslint:disable-next-line:no-conditional-assignment + while ((match = regex.exec(replacement)) !== null) { + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + + const m = match[1]; + + const envVar: string = env.get(m, m).asString(); + + replacement = replacement.replace("${env:" + m + "}", envVar); + } + + return replacement; +} |