diff options
| author | Christoph Schlosser <2466365+cschlosser@users.noreply.github.com> | 2021-04-17 22:12:38 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-17 22:12:38 +0200 |
| commit | dbcf5dc87d83ee59fc23e4eaa225a15655d17d2d (patch) | |
| tree | d22cb94be324153581658e80a8596765755971a0 /src | |
| parent | 703019a593c1e4118b580015b1af8bcfbb86b135 (diff) | |
| download | doxdocgen-dbcf5dc87d83ee59fc23e4eaa225a15655d17d2d.tar.gz | |
Prepare release 1.2.0 (#213)1.2.0
* Prepare release 1.2.0
* Update versions
* Lint
* update appveyor
* Update travis
* Use yarn
* Add nyc
* Run cov instead of test
* remove package-lock.json
* Remove istanbul
Diffstat (limited to 'src')
| -rw-r--r-- | src/DoxygenCompletionItemProvider.ts | 48 | ||||
| -rw-r--r-- | src/extension.ts | 26 | ||||
| -rw-r--r-- | src/test/index.ts | 277 | ||||
| -rw-r--r-- | src/test/runTests.ts | 28 |
4 files changed, 128 insertions, 251 deletions
diff --git a/src/DoxygenCompletionItemProvider.ts b/src/DoxygenCompletionItemProvider.ts index 54baaaa..b71b827 100644 --- a/src/DoxygenCompletionItemProvider.ts +++ b/src/DoxygenCompletionItemProvider.ts @@ -1,17 +1,19 @@ import * as vscode from "vscode"; + +// tslint:disable:max-line-length + /*https://github.com/cschlosser/doxdocgen/issues/30 */ -export default class DoxygenCompletionItemProvider implements vscode.CompletionItemProvider -{ +export default class DoxygenCompletionItemProvider implements vscode.CompletionItemProvider { /** * commands are a tuple of <command, snippet, documentation> */ - static readonly commands: [string, string, string][] = [ + public static readonly commands: Array<[string, string, string]> = [ /*Special commands */ ["a", "${1:word}", "Display `<word>` in italics"], ["arg", "${1:item-description}", "Generate a simple, non-nested list of arguments"], ["b", "${1:word}", "Display `<word>` in bold"], ["c", "${1:word}", "Dispaly `<word>` using a typewriter font"], - ["code", ".${1:language-id}\n${2:code}\n@endcode", "Starts a block of code"], //TODO: match end block symbol with trigger character + ["code", ".${1:language-id}\n${2:code}\n@endcode", "Starts a block of code"], // TODO: match end block symbol with trigger character ["copydoc", "${1:link-object}", "Copy a documentation block from the object specified by `<link-object>` and paste it at the location of the command. The link object can point to a member (of a class, file or group), a class, a namespace, a group, a page, or a file. If the memeber if overloaded, you should specify the argument types explicitly"], ["copybrief", "${1:link-object}", "Work in a similar way as `@copydoc` but will only copy the brief description, not the detailed documentation"], ["copydetails", "${1:link-object}", "Work in a similar way as `@copydoc` but will only copy the detailed documentation, not the brief description"], @@ -100,7 +102,7 @@ export default class DoxygenCompletionItemProvider implements vscode.CompletionI ["include", "{${1|lineno,doc|}} ${2:file-name}", "This command can be used to include a source file as a block of code. Using the `@include` command is equivalent to inserting the file into the documentation block and surrounding it with `@code` and `@endcode` commands."], ["line", "${1:pattern}", "This command searches line by line through the example that was last included using `@include` or `@dontinclude` until it finds a non-blank line. If that line contains the specified pattern, it is written to the output."], ["skip", "${1:pattern}", "This command searches line by line through the example that was last included using `@include` or `@dontinclude` until it finds a line that contains the specified pattern."], - ["skipline", "${1:pattern}", "This command searches line by line through the example that was last included using `@include` or `@dontinclude` until it finds a line that contains the specified pattern. It then writes the line to the output.",], + ["skipline", "${1:pattern}", "This command searches line by line through the example that was last included using `@include` or `@dontinclude` until it finds a line that contains the specified pattern. It then writes the line to the output."], ["snippet", "{${1|lineno,doc|}} ${2:file-name} ${3:block_id}", "Where the `@include` command can be used to include a complete file as source code, this command can be used to quote only a fragment of a source file. In case this is used as `<file-name>` the current file is taken as file to take the snippet from."], ["until", "${1:pattern}", "This command writes all lines of the example that was last included using `@include` or `@dontinclude` to the output, until it finds a line containing the specified pattern. The line containing the pattern will be written as well."], ["verbinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` verbatim in the documentation. The command is equivalent to pasting the contents of the file in the documentation and placing `@verbatim` and `@endverbatim` commands around it."], @@ -109,14 +111,12 @@ export default class DoxygenCompletionItemProvider implements vscode.CompletionI ["rtfinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the RTF documentation and tagged with `<rtfonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@rtfonly` and `@endrtfonly` commands around it."], ["maninclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the MAN documentation and tagged with `<manonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@manonly` and `@endmanonly` commands around it."], ["docbookinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the DocBook documentation and tagged with `<docbookonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@docbookonly` and `@enddocbookonly` commands around it."], - ["xmlinclude", "${1:file-name}", "This command includes contents of the the file `<file-name>` as is in the XML documentation. The command is equivalent to pasting the contents of the file in the documentation and placing `@xmlonly` and `@endxmlonly` commands around it."] + ["xmlinclude", "${1:file-name}", "This command includes contents of the the file `<file-name>` as is in the XML documentation. The command is equivalent to pasting the contents of the file in the documentation and placing `@xmlonly` and `@endxmlonly` commands around it."], ]; - static completionItems = (() => - { - let items: vscode.CompletionItem[] = []; - for (const item of DoxygenCompletionItemProvider.commands) - { - let newItem = new vscode.CompletionItem(item[0]); + public static completionItems = (() => { + const items: vscode.CompletionItem[] = []; + for (const item of DoxygenCompletionItemProvider.commands) { + const newItem = new vscode.CompletionItem(item[0]); newItem.documentation = new vscode.MarkdownString(item[2]); newItem.insertText = new vscode.SnippetString(`${item[0]} ${item[1]}`); newItem.kind = vscode.CompletionItemKind.Snippet; @@ -125,31 +125,31 @@ export default class DoxygenCompletionItemProvider implements vscode.CompletionI return items; })(); - trigger: string = ""; - indentSpace: number; - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) - { + public trigger: string = ""; + public indentSpace: number; + public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) { this.trigger = context.triggerCharacter; this.indentSpace = position.character; - //TODO: check if current position is comment + // TODO: check if current position is comment return DoxygenCompletionItemProvider.completionItems; } - resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken) - { + public resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken) { let insertion = (item.insertText as vscode.SnippetString).value; - if (this.trigger === "\\") + if (this.trigger === "\\") { insertion = insertion.replace("@", "\\"); + } const indentPrefix = "\n".concat("* "); insertion = insertion.replace(/\n/g, indentPrefix); - + /*insert an empty line if the snippet is multi-line, so * can be auto-completed */ - if (insertion.includes("\n")) + if (insertion.includes("\n")) { insertion = insertion.concat(indentPrefix); + } - let newItem = new vscode.CompletionItem(item.label, item.kind); + const newItem = new vscode.CompletionItem(item.label, item.kind); newItem.documentation = item.documentation; newItem.insertText = new vscode.SnippetString(insertion); return newItem; } -}
\ No newline at end of file +} diff --git a/src/extension.ts b/src/extension.ts index b7c148c..dfd38c5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,16 +6,14 @@ import CodeParserController from "./CodeParserController"; import DoxygenCompletionItemProvider from "./DoxygenCompletionItemProvider"; enum Version { - CURRENT = "1.1.0", - PREVIOUS = "1.0.1", + CURRENT = "1.2.0", + PREVIOUS = "1.1.0", KEY = "doxdocgen_version", } // this method is called when your extension is activated // your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) -{ - +export function activate(context: vscode.ExtensionContext) { const parser = new CodeParserController(); context.subscriptions.push(parser); @@ -28,15 +26,17 @@ export function activate(context: vscode.ExtensionContext) } /*register doxygen commands intellisense */ - if (vscode.workspace.getConfiguration("doxdocgen.generic").get<boolean>("commandSuggestion")) + if (vscode.workspace.getConfiguration("doxdocgen.generic").get<boolean>("commandSuggestion")) { + // tslint:disable-next-line: max-line-length vscode.languages.registerCompletionItemProvider({ language: "cpp", scheme: "file" }, new DoxygenCompletionItemProvider(), "@", "\\"); - - //After the CompletionItemProvider is registered, it cannot be unregistered - //Check the settings everytime when it is triggered would be inefficient - //So just prompt the user to restart to take effect - vscode.workspace.onDidChangeConfiguration(event => - { - if (event.affectsConfiguration("doxdocgen.generic.commandSuggestion")) + } + + // After the CompletionItemProvider is registered, it cannot be unregistered + // Check the settings everytime when it is triggered would be inefficient + // So just prompt the user to restart to take effect + vscode.workspace.onDidChangeConfiguration((event) => { + if (event.affectsConfiguration("doxdocgen.generic.commandSuggestion")) { vscode.window.showWarningMessage("Please restart vscode to apply the changes!"); + } }); } diff --git a/src/test/index.ts b/src/test/index.ts index 5dfa541..89f953b 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -1,220 +1,69 @@ -"use strict"; - -import * as fs from "fs"; import * as glob from "glob"; -import * as paths from "path"; - -import istanbul = require("istanbul"); -import remapIstanbul = require("remap-istanbul"); -// tslint:disable-next-line:no-var-requires -const tty = require("tty"); -// tslint:disable-next-line:no-var-requires -const Mocha = require("mocha"); - -// Linux: prevent a weird NPE when mocha on Linux requires the window size from the TTY -// Since we are not running in a tty environment, we just implementt he method statically -if (!tty.getWindowSize) { - tty.getWindowSize = (): number[] => { - return [80, 75]; - }; -} - -let mocha = new Mocha({ - ui: "tdd", - useColors: true, -}); - -function configure(mochaOpts): void { - mocha = new Mocha(mochaOpts); -} -exports.configure = configure; - -function _mkDirIfExists(dir: string): void { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } -} - -function _readCoverOptions(testsRoot: string): ITestRunnerOptions { - const coverConfigPath = paths.join(testsRoot, "..", "..", "coverconfig.json"); - let coverConfig: ITestRunnerOptions; - if (fs.existsSync(coverConfigPath)) { - const configContent = fs.readFileSync(coverConfigPath, "utf-8"); - coverConfig = JSON.parse(configContent); - } - return coverConfig; -} - -function run(testsRoot, clb): any { - // Enable source map support - require("source-map-support").install(); - - // Read configuration for the coverage file - const coverOptions: ITestRunnerOptions = _readCoverOptions(testsRoot); - if (coverOptions && coverOptions.enabled) { - // Setup coverage pre-test, including post-test hook to report - const coverageRunner = new CoverageRunner(coverOptions, testsRoot, clb); - coverageRunner.setupCoverage(); - } - - // Glob test files - glob("**/**.test.js", { cwd: testsRoot }, (error, files): any => { - if (error) { - return clb(error); - } - try { - // Fill into Mocha - files.forEach((f): Mocha => { - return mocha.addFile(paths.join(testsRoot, f)); - }); - // Run the tests - let failureCount = 0; - - mocha.run() - .on("fail", (test, err): void => { - failureCount++; - }) - .on("end", (): void => { - clb(undefined, failureCount); - }); - } catch (error) { - return clb(error); - } +import * as Mocha from "mocha"; +import * as path from "path"; + +function setupNyc() { + const NYC = require("nyc"); + // create an nyc instance, config here is the same as your package.json + const nyc = new NYC({ + cache: false, + cwd: path.join(__dirname, "..", ".."), + exclude: [ + "**/**.test.js", + ], + extension: [ + ".ts", + ".tsx", + ], + hookRequire: true, + hookRunInContext: true, + hookRunInThisContext: true, + instrument: true, + reporter: ["text", "lcov", "cobertura"], + require: [ + "ts-node/register", + "source-map-support/register", + ], + sourceMap: true, }); + nyc.reset(); + nyc.wrap(); + return nyc; } -exports.run = run; - -interface ITestRunnerOptions { - enabled?: boolean; - relativeCoverageDir: string; - relativeSourcePath: string; - ignorePatterns: string[]; - includePid?: boolean; - reports?: string[]; - verbose?: boolean; -} - -class CoverageRunner { - - private coverageVar: string = "$$cov_" + new Date().getTime() + "$$"; - private transformer: any = undefined; - private matchFn: any = undefined; - private instrumenter: any = undefined; - - constructor(private options: ITestRunnerOptions, private testsRoot: string, private endRunCallback: any) { - if (!options.relativeSourcePath) { - return endRunCallback("Error - relativeSourcePath must be defined for code coverage to work"); - } - - } - public setupCoverage(): void { - // Set up Code Coverage, hooking require so that instrumented code is returned - this.instrumenter = new istanbul.Instrumenter({ coverageVariable: this.coverageVar }); - const sourceRoot = paths.join(this.testsRoot, this.options.relativeSourcePath); - - // Glob source files - const srcFiles = glob.sync("**/**.js", { - cwd: sourceRoot, - ignore: this.options.ignorePatterns, - }); - - // Create a match function - taken from the run-with-cover.js in istanbul. - const decache = require("decache"); - const fileMap = {}; - srcFiles.forEach( (file) => { - const fullPath = paths.join(sourceRoot, file); - fileMap[fullPath] = true; - - // On Windows, extension is loaded pre-test hooks and this mean we lose - // our chance to hook the Require call. In order to instrument the code - // we have to decache the JS file so on next load it gets instrumented. - // This doesn"t impact tests, but is a concern if we had some integration - // tests that relied on VSCode accessing our module since there could be - // some shared global state that we lose. - decache(fullPath); - }); - - this.matchFn = (file): boolean => fileMap[file]; - this.matchFn.files = Object.keys(fileMap); - - // Hook up to the Require function so that when this is called, if any of our source files - // are required, the instrumented version is pulled in instead. These instrumented versions - // write to a global coverage variable with hit counts whenever they are accessed - this.transformer = this.instrumenter.instrumentSync.bind(this.instrumenter); - const hookOpts = { verbose: false, extensions: [".js"]}; - istanbul.hook.hookRequire(this.matchFn, this.transformer, hookOpts); - - // initialize the global variable to stop mocha from complaining about leaks - global[this.coverageVar] = {}; - - // Hook the process exit event to handle reporting - // Only report coverage if the process is exiting successfully - process.on("exit", (code) => { - this.reportCoverage(); - }); - } - - /** - * Writes a coverage report. - * Note that as this is called in the process exit callback, all calls must be synchronous. - * - * @returns {void} - * - * @memberOf CoverageRunner - */ - public reportCoverage(): void { - istanbul.hook.unhookRequire(); - let cov: any; - if (typeof global[this.coverageVar] === "undefined" || Object.keys(global[this.coverageVar]).length === 0) { - // tslint:disable:no-console - console.error("No coverage information was collected, exit without writing coverage information"); - return; - } else { - cov = global[this.coverageVar]; - } - - // TODO consider putting this under a conditional flag - // Files that are not touched by code ran by the test runner is manually instrumented, to - // illustrate the missing coverage. - this.matchFn.files.forEach( (file) => { - if (!cov[file]) { - this.transformer(fs.readFileSync(file, "utf-8"), file); - - // When instrumenting the code, istanbul will give each FunctionDeclaration a value of 1 in - // coverState.s, presumably to compensate for function hoisting. We need to reset this, as the function - // was not hoisted, as it was never loaded. - Object.keys(this.instrumenter.coverState.s).forEach( (key) => { - this.instrumenter.coverState.s[key] = 0; - }); - - cov[file] = this.instrumenter.coverState; - } - }); - - // TODO Allow config of reporting directory with - const reportingDir = paths.join(this.testsRoot, this.options.relativeCoverageDir); - const includePid = this.options.includePid; - const pidExt = includePid ? ("-" + process.pid) : ""; - const coverageFile = paths.resolve(reportingDir, "coverage" + pidExt + ".json"); - - // yes, do this again since some test runners could clean the dir initially created - _mkDirIfExists(reportingDir); - fs.writeFileSync(coverageFile, JSON.stringify(cov), "utf8"); - - const remappedCollector = remapIstanbul.remap(cov, {warn: (warning) => { - // We expect some warnings as any JS file without a typescript mapping will cause this. - // By default, we"ll skip printing these to the console as it clutters it up - if (this.options.verbose) { - console.warn(warning); - } - }}); - - const reporter = new istanbul.Reporter(undefined, reportingDir); - const reportTypes = (this.options.reports instanceof Array) ? this.options.reports : ["lcov"]; - reporter.addAll(reportTypes); - reporter.write(remappedCollector, true, () => { - console.log(`reports written to ${reportingDir}`); +export function run(): Promise<void> { + // Create the mocha test + const mocha = new Mocha({ + ui: "tdd", + }); + + const nyc = setupNyc(); + const testsRoot = path.resolve(__dirname, "."); + + return new Promise((c, e) => { + glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { + if (err) { + return e(err); + } + + // Add files to the test suite + files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + mocha.run((failures) => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } }); - } + } catch (err) { + e(err); + } finally { + nyc.writeCoverageFile(); + nyc.report(); + } + }); + }); } diff --git a/src/test/runTests.ts b/src/test/runTests.ts new file mode 100644 index 0000000..70c2381 --- /dev/null +++ b/src/test/runTests.ts @@ -0,0 +1,28 @@ +import * as path from "path"; + +import { runTests } from "vscode-test"; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, "../../"); + + // The path to the extension test runner script + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, "./index"); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + // tslint:disable-next-line: no-console + console.error("Got error:"); + // tslint:disable-next-line: no-console + console.error(err); + // tslint:disable-next-line: no-console + console.error("Failed to run tests"); + process.exit(1); + } +} + +main(); |