diff --git a/.vscode/launch.json b/.vscode/launch.json index 0ef00fd..3d30f88 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,7 +20,7 @@ "runtimeExecutable": "${execPath}", "args": [ "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/out/test" + "--extensionTestsPath=${workspaceFolder}/src/test/integration/index.ts" ], "outFiles": [ "${workspaceFolder}/out/test/**/*.js" @@ -28,4 +28,4 @@ "preLaunchTask": "npm: compile" } ] -} +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bacdb1c..609b3c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [0.5.5] - 2021-01-17 + +- Added class and module docstrings (@tracetidwell) + ## [0.5.4] - 2020-11-17 - Added Starlark support #139 (@UebelAndre) diff --git a/README.md b/README.md index 2627df5..4599839 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Visual Studio Code extension to quickly generate docstrings for python functions ## Usage -Cursor must be on the line directly below the definition to generate full auto-populated docstring +Cursor must be on the line directly below the class or method definition or on the first line of the file to generate full auto-populated docstring - Press enter after opening docstring with triple quotes (`"""` or `'''`) - Keyboard shortcut: `ctrl+shift+2` or `cmd+shift+2` for mac @@ -111,6 +111,15 @@ This extension now supports custom templates. The extension uses the [mustache.j {{#returnsExist}} - display contents if returns exist {{/returnsExist}} +{{#classesExist}} - display contents if classes exist +{{/classesExist}} + +{{#methodsExist}} - display contents if methods exist +{{/methodsExist}} + +{{#attributesExist}} - display contents if attributes exist +{{/attributesExist}} + {{#placeholder}} - makes contents a placeholder {{/placeholder}} ``` diff --git a/module_docstring.md b/module_docstring.md new file mode 100644 index 0000000..2a41b23 --- /dev/null +++ b/module_docstring.md @@ -0,0 +1,33 @@ +# Changes made to autoDocstring to incorporate module level docstrings +- src/parse/get_body.ts + - getBody(): if docstring is being added at first line of document, return entire document as body +- src/docstring_parts.ts + - DocstringParts: added Class[] and Method[] attributes + - Class: Created Class interface + - Method: Created Method interface +- src/parse/parse_parameters.ts + - parseParameters(): added positionLine argument, if positionLine == 0 return only top-level classes and methods, otherwise return normal DocstringParts + - parseClasses(): returns list of top-level classes + - parseMethods(): returns list of top-level methods +- src/docstring/template_data.ts + - TemplateData: added classes and methods attributes, updated constructor to take list of classes and list of methods + - classesExist(): added method to check if classes exist, needed to parse template + - methodsExist(): added method to check if methods exist, needed to parse template +- src/docstring/templates/ncr.mustache + - Created template file that incorporates classes and methods +- package.json + - contributes.configuration.properties.autoDocstring.docstringFormat.enum + - Added "ncr" template (this should probably be removed) + - contributes.commands[0].title + - Changed to "Generate Docstring2" just to avoid any confusion, will change back when ready for PR + +# To run the code +- Open the autoDocstring folder in VS Code +- Open the command palette (Ctrl+Shift+P) and type `settings.json` to open the settings configuration file +- Add the line `"autoDocstring.customTemplatePath": "/home/shared/Projects/autoDocstring/src/docstring/templates/ncr.mustache"` and make sure the path to the ncr.mustache template is correct for your computer. +- Save and close settings.json. +- Press F5 +- Click "Debug Anyway" +- When the new screen opens, open a folder to any project you want to add a module level docstring to +- On line 1, click Ctrl+Shift+P or input """ and press enter. +- The docstring should populate and list all top-level classes and methods in the file. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d30bba1..0567cf7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,22 @@ "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", "dev": true }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, "@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", diff --git a/package.json b/package.json index 07e0f72..631f7e9 100644 --- a/package.json +++ b/package.json @@ -71,12 +71,13 @@ "properties": { "autoDocstring.docstringFormat": { "type": "string", - "default": "google", + "default": "ncr", "enum": [ "docblockr", "google", "sphinx", - "numpy" + "numpy", + "ncr" ], "description": "Which docstring format to use." }, @@ -145,6 +146,7 @@ }, "devDependencies": { "@types/chai": "^4.2.11", + "@types/glob": "^7.1.3", "@types/mocha": "^5.2.7", "@types/mustache": "^0.8.32", "@types/node": "^13.13.1", diff --git a/src/docstring/docstring_factory.ts b/src/docstring/docstring_factory.ts index d4e97c9..18efbb5 100644 --- a/src/docstring/docstring_factory.ts +++ b/src/docstring/docstring_factory.ts @@ -27,6 +27,8 @@ export class DocstringFactory { this.includeDescription = includeDescription; this.template = template; + // console.log('template') + // console.log(this.template) } public generateDocstring(docstringParts: DocstringParts, indentation = ""): string { diff --git a/src/docstring/template_data.ts b/src/docstring/template_data.ts index acbc310..34104cc 100644 --- a/src/docstring/template_data.ts +++ b/src/docstring/template_data.ts @@ -6,8 +6,11 @@ import { KeywordArgument, Returns, Yields, + Method, + Attribute } from "../docstring_parts"; + export class TemplateData { public name: string; public decorators: Decorator[]; @@ -16,6 +19,9 @@ export class TemplateData { public exceptions: Exception[]; public returns: Returns; public yields: Yields; + public classes: Method[]; + public methods: Method[]; + public attributes: Attribute[]; private includeName: boolean; private includeExtendedSummary: boolean; @@ -33,6 +39,9 @@ export class TemplateData { this.exceptions = docstringParts.exceptions; this.returns = docstringParts.returns; this.yields = docstringParts.yields; + this.classes = docstringParts.classes; + this.methods = docstringParts.methods; + this.attributes = docstringParts.attributes; this.includeName = includeName; this.includeExtendedSummary = includeExtendedSummary; @@ -99,6 +108,18 @@ export class TemplateData { return this.yields != undefined; } + public classesExist(): boolean { + return this.classes.length > 0; + } + + public methodsExist(): boolean { + return this.methods.length > 0; + } + + public attributesExist(): boolean { + return this.attributes.length > 0; + } + private removeTypes(): void { for (const arg of this.args) { arg.type = undefined; @@ -139,5 +160,11 @@ export class TemplateData { if (yields != undefined && yields.type == undefined) { yields.type = placeholder; } + + for (const attribute of this.attributes) { + if (attribute.type === undefined) { + attribute.type = placeholder; + } + } } } diff --git a/src/docstring/templates/ncr.mustache b/src/docstring/templates/ncr.mustache new file mode 100644 index 0000000..59aa18c --- /dev/null +++ b/src/docstring/templates/ncr.mustache @@ -0,0 +1,75 @@ +{{! NCR Docstring Template }} +{{summaryPlaceholder}} + +{{extendedSummaryPlaceholder}} + +{{#classesExist}} +Classes +------- +{{#classes}} +{{name}} +{{/classes}} +{{/classesExist}} + +{{#methodsExist}} +Methods +------- +{{#methods}} +{{name}} +{{/methods}} +{{/methodsExist}} + +{{#parametersExist}} +Parameters +---------- +{{#args}} +{{var}} : {{typePlaceholder}} + {{descriptionPlaceholder}} + +{{/args}} +{{#kwargs}} +{{var}} : {{typePlaceholder}}, optional + {{descriptionPlaceholder}}, by default {{&default}} + +{{/kwargs}} +{{/parametersExist}} + +{{#attributesExist}} +Attributes +---------- +{{#attributes}} +{{var}} : {{typePlaceholder}} + {{descriptionPlaceholder}} + +{{/attributes}} +{{/attributesExist}} + +{{#returnsExist}} +Returns +------- +{{#returns}} +return_val : {{typePlaceholder}} + {{descriptionPlaceholder}} + +{{/returns}} +{{/returnsExist}} + +{{#yieldsExist}} +Yields +------- +{{#yields}} +yield_val : {{typePlaceholder}} + {{descriptionPlaceholder}} + +{{/yields}} +{{/yieldsExist}} + +{{#exceptionsExist}} +Raises +------ +{{#exceptions}} +{{type}} + {{descriptionPlaceholder}} + +{{/exceptions}} +{{/exceptionsExist}} diff --git a/src/docstring/templates/sphinx.mustache b/src/docstring/templates/sphinx.mustache index b7ba7d7..502805d 100644 --- a/src/docstring/templates/sphinx.mustache +++ b/src/docstring/templates/sphinx.mustache @@ -3,6 +3,22 @@ {{extendedSummaryPlaceholder}} +{{#classesExist}} +Classes +------- +{{#classes}} +{{name}} +{{/classes}} +{{/classesExist}} + +{{#methodsExist}} +Methods +------- +{{#methods}} +{{name}} +{{/methods}} +{{/methodsExist}} + {{#args}} :param {{var}}: {{descriptionPlaceholder}} :type {{var}}: {{typePlaceholder}} diff --git a/src/docstring_parts.ts b/src/docstring_parts.ts index b6d9218..995f6b1 100644 --- a/src/docstring_parts.ts +++ b/src/docstring_parts.ts @@ -6,6 +6,9 @@ export interface DocstringParts { exceptions: Exception[]; returns: Returns; yields: Yields; + classes: Method[]; + methods: Method[]; + attributes: Attribute[]; } export interface Decorator { @@ -34,3 +37,16 @@ export interface Returns { export interface Yields { type: string; } + +// export interface Class { +// name: string; +// } + +export interface Method { + name: string; +} + +export interface Attribute { + var: string; + type: string; +} \ No newline at end of file diff --git a/src/parse/get_body.ts b/src/parse/get_body.ts index 064bb80..4732ab9 100644 --- a/src/parse/get_body.ts +++ b/src/parse/get_body.ts @@ -1,12 +1,30 @@ import { blankLine, indentationOf, preprocessLines } from "./utilities"; -export function getBody(document: string, linePosition: number): string[] { +export function getBody(docstringType: string, document: string, linePosition: number): string[] { const lines = document.split("\n"); - const body = []; + let regex = '\s*def \w+' + + if (docstringType === 'module') { + return lines; + } + else if (docstringType === 'class') { + regex = '.'; + } let currentLineNum = linePosition; - const originalIndentation = getBodyBaseIndentation(lines, linePosition); + const originalIndentation = getBodyBaseIndentation(lines, linePosition, regex); + + if (originalIndentation === 0) { + return []; + } + return populateBody(currentLineNum, lines, originalIndentation); + +} + +function populateBody(currentLineNum: number, lines: string[], originalIndentation: number) { + + const body = []; while (currentLineNum < lines.length) { const line = lines[currentLineNum]; @@ -26,9 +44,10 @@ export function getBody(document: string, linePosition: number): string[] { return preprocessLines(body); } -function getBodyBaseIndentation(lines: string[], linePosition: number): number { +function getBodyBaseIndentation(lines: string[], linePosition: number, regex: string): number { let currentLineNum = linePosition; - const functionDefRegex = /\s*def \w+/; + //const functionDefRegex = /\s*def \w+/; + const functionDefRegex = RegExp(regex) while (currentLineNum < lines.length) { const line = lines[currentLineNum]; diff --git a/src/parse/get_class_name.ts b/src/parse/get_class_name.ts new file mode 100644 index 0000000..d09634c --- /dev/null +++ b/src/parse/get_class_name.ts @@ -0,0 +1,18 @@ +export function getClassName(functionDefinition: string): string { + //const pattern1 = /(?:class)\s+(\w+)\s*\(/; + const pattern1 = /(?:class)\s+(\w+)/; + const pattern2 = /(?:class)\s+(\w+)/; + + const match1 = pattern1.exec(functionDefinition); + const match2 = pattern2.exec(functionDefinition); + + if (match1 != undefined && match1[1] != undefined) { + return match1[1]; + } + else if (match2 != undefined && match2[1] != undefined) { + return match2[1] + } + else { + return ""; + } +} diff --git a/src/parse/get_definition.ts b/src/parse/get_definition.ts index 5e6dd3c..8c6ce8b 100644 --- a/src/parse/get_definition.ts +++ b/src/parse/get_definition.ts @@ -1,6 +1,11 @@ -import { blankLine, preprocessLines } from "./utilities"; +import { blankLine, indentationOf, preprocessLines } from "./utilities"; export function getDefinition(document: string, linePosition: number): string { + + if (linePosition === 0) { + return ""; + } + const precedingLines = getPrecedingLines(document, linePosition); const precedingText = precedingLines.join(" "); @@ -10,6 +15,70 @@ export function getDefinition(document: string, linePosition: number): string { return ""; } + const index = getIndex(precedingText) + if (index == undefined) { + return ""; + } + + let lastFunctionDef = precedingText.slice(index).trim(); + if (lastFunctionDef.startsWith('class')) { + lastFunctionDef = getClassDefinition(document.split("\n"), lastFunctionDef, linePosition) + } + + return lastFunctionDef; +} + +function getClassDefinition(lines: string[], lastFunctionDef: string, linePosition: number): string { + let definition = getClassName(lastFunctionDef); + + while (linePosition < lines.length) { + const line = lines[linePosition]; + definition = updateDefinition(definition, line) + + if (isCloseDefMatch(line)) { + return definition + } + linePosition += 1; + } + + return definition +} + +function updateDefinition(definition: string, line: string): string { + if (isInitMatch(line)) { + definition += getInitMatch(line); + } + else { + definition += line.trim(); + } + return definition +} + +function isInitMatch(line: string): boolean { + // const initPattern = /(?<=def __init__).*/; + const initPattern = /(?:def __init__)/; + const initMatch = initPattern.exec(line) + return initMatch != undefined && initMatch[0] != undefined; +} + +function getInitMatch(line: string): string { + const initPattern = /(?<=def __init__).*/; + return initPattern.exec(line)[0].trim() +} + +function isCloseDefMatch(line: string): boolean { + const defClosePattern = /(\))/ + const defCloseMatch = defClosePattern.exec(line); + return defCloseMatch != undefined && defCloseMatch[0] != undefined; +} + +function getClassName(lastFunctionDef: string): string { + const classPattern = /(?:class)\s+(\w+)/; + const classMatch = classPattern.exec(lastFunctionDef); + return classMatch[0]; +} + +function getIndex(precedingText: string): number { const pattern = /\b(((async\s+)?\s*def)|\s*class)\b/g; // Get starting index of last def match in the preceding text @@ -18,12 +87,7 @@ export function getDefinition(document: string, linePosition: number): string { index = pattern.lastIndex - RegExp.lastMatch.length; } - if (index == undefined) { - return ""; - } - - const lastFunctionDef = precedingText.slice(index); - return lastFunctionDef.trim(); + return index } function getPrecedingLines(document: string, linePosition: number): string[] { @@ -31,6 +95,5 @@ function getPrecedingLines(document: string, linePosition: number): string[] { const rawPrecedingLines = lines.slice(0, linePosition); const precedingLines = preprocessLines(rawPrecedingLines); - return precedingLines; -} +} \ No newline at end of file diff --git a/src/parse/get_docstring_indentation.ts b/src/parse/get_docstring_indentation.ts index 3002d3c..f7882d0 100644 --- a/src/parse/get_docstring_indentation.ts +++ b/src/parse/get_docstring_indentation.ts @@ -5,6 +5,10 @@ export function getDocstringIndentation( linePosition: number, defaultIndentation: string, ): string { + + if (linePosition === 0) { + return "" + } const lines = document.split("\n"); const definitionPattern = /\b(((async\s+)?\s*def)|\s*class)\b/g; diff --git a/src/parse/get_docstring_type.ts b/src/parse/get_docstring_type.ts new file mode 100644 index 0000000..5aad09a --- /dev/null +++ b/src/parse/get_docstring_type.ts @@ -0,0 +1,18 @@ +export function getDocstringType(functionDefinition: string, linePosition: number): string { + + const class_pattern = /(?:class)/; + const class_match = class_pattern.exec(functionDefinition); + + if (linePosition === 0) { + return "module" + } + + else if (class_match != undefined && class_match[0] != undefined) { + return "class"; + } + + else { + return "method"; + } + +} diff --git a/src/parse/get_function_name.ts b/src/parse/get_function_name.ts index 063e0a1..f7d4ee0 100644 --- a/src/parse/get_function_name.ts +++ b/src/parse/get_function_name.ts @@ -1,5 +1,5 @@ export function getFunctionName(functionDefinition: string): string { - const pattern = /(?:def|class)\s+(\w+)\s*\(/; + const pattern = /(?:def|class)\s+(\w+)\s*\(*/; const match = pattern.exec(functionDefinition); diff --git a/src/parse/index.ts b/src/parse/index.ts index 915625c..681c271 100644 --- a/src/parse/index.ts +++ b/src/parse/index.ts @@ -8,4 +8,5 @@ export { validDocstringPrefix } from "./valid_docstring_prefix"; export { parse } from "./parse"; export { parseParameters } from "./parse_parameters"; export { tokenizeDefinition } from "./tokenize_definition"; +export { getDocstringType } from "./get_docstring_type"; export { getDefaultIndentation } from "./utilities"; diff --git a/src/parse/parse.ts b/src/parse/parse.ts index c494ab2..3ce8643 100644 --- a/src/parse/parse.ts +++ b/src/parse/parse.ts @@ -1,16 +1,19 @@ -import { getBody, getDefinition, getFunctionName, parseParameters, tokenizeDefinition } from "."; +import { getBody, getDefinition, getFunctionName, parseParameters, tokenizeDefinition, getDocstringType } from "."; import { DocstringParts } from "../docstring_parts"; // import * as vs from "vscode"; export function parse(document: string, positionLine: number): DocstringParts { // vs.window.showErrorMessage("here"); const definition = getDefinition(document, positionLine); - // vs.window.showErrorMessage("here2"); - const body = getBody(document, positionLine); - // vs.window.showErrorMessage("here3"); - + const docstringType = getDocstringType(definition, positionLine) + const body = getBody(docstringType, document, positionLine); const parameterTokens = tokenizeDefinition(definition); const functionName = getFunctionName(definition); - return parseParameters(parameterTokens, body, functionName); + console.log(docstringType) + console.log(body) + console.log(parameterTokens) + console.log(functionName) + + return parseParameters(docstringType, parameterTokens, body, functionName); } diff --git a/src/parse/parse_parameters.ts b/src/parse/parse_parameters.ts index a2c6122..5e358ba 100644 --- a/src/parse/parse_parameters.ts +++ b/src/parse/parse_parameters.ts @@ -1,5 +1,7 @@ import { guessType } from "."; -import { +import { indentationOf } from "./utilities"; +import { getFunctionName } from "./get_function_name"; +import { Argument, Decorator, DocstringParts, @@ -7,21 +9,79 @@ import { KeywordArgument, Returns, Yields, + Method, + Attribute } from "../docstring_parts"; +function parseMethod( + parameterTokens: string[], + body: string[], + functionName: string): DocstringParts { + return { + name: functionName, + decorators: parseDecorators(parameterTokens), + args: parseArguments(parameterTokens), + kwargs: parseKeywordArguments(parameterTokens), + returns: parseReturn(parameterTokens, body), + yields: parseYields(parameterTokens, body), + exceptions: parseExceptions(body), + classes: [], + methods: [], + attributes: [] + }; +} + +function parseClass( + parameterTokens: string[], + body: string[], + functionName: string): DocstringParts { + let args = parseArguments(parameterTokens); + let kwargs = parseKeywordArguments(parameterTokens); + return { + name: functionName, + decorators: parseDecorators(parameterTokens), + args: args, + kwargs: kwargs, + returns: undefined, + yields: undefined, + exceptions: [], + classes: [], + methods: [], + attributes: parseAttributes(body, args, kwargs) + }; +} + +function parseModule( + body: string[], + functionName: string): DocstringParts { + return { + name: functionName, + decorators: [], + args: [], + kwargs: [], + returns: undefined, + yields: undefined, + exceptions: [], + classes: parseMethods(body, /(?:class)\s/), + methods: parseMethods(body, /(def)\s+(\w+)\s*\(/), + attributes: [] + }; +} + export function parseParameters( + docstringType: string, parameterTokens: string[], body: string[], - functionName: string, -): DocstringParts { - return { - name: functionName, - decorators: parseDecorators(parameterTokens), - args: parseArguments(parameterTokens), - kwargs: parseKeywordArguments(parameterTokens), - returns: parseReturn(parameterTokens, body), - yields: parseYields(parameterTokens, body), - exceptions: parseExceptions(body), + functionName: string): DocstringParts { + + if (docstringType === "module") { + return parseModule(body, functionName); + } + else if (docstringType === "method") { + return parseMethod(parameterTokens, body, functionName); + } + else if (docstringType === "class") { + return parseClass(parameterTokens, body, functionName); }; } @@ -148,6 +208,47 @@ function parseExceptions(body: string[]): Exception[] { return exceptions; } +function parseMethods(body: string[], pattern: RegExp): Method[] { + const methods: Method[] = [] + // const pattern = /(def)\s+(\w+)\s*\(/; + // const pattern = /\b(((async\s+)?\s*def)|\s*class)\b/g; + + for (const line of body) { + + const match = line.match(pattern); + if (indentationOf(line) === 0 && match != null) { + methods.push({ + name: getFunctionName(line), + }); + } + } + return methods; +} + +function parseAttributes(body: string[], args: Argument[], kwargs: KeywordArgument[]): Attribute[] { + const attributes: Attribute[] = []; + const pattern = /(?:self\.|cls\.)(\w+)(?:\s*:[^=]+)?\s*=\s*(.+)/; + //const pattern = /(?:self).(\w+)?\s*/ + + for (const line of body) { + const match = line.trim().match(pattern); + + if (match == null) { + continue; + } + let var_ = match[1]; + let type_ = guessType(match[1]); + if (!containsAttribute(attributes, var_) && !containsAttribute(args, var_) && !containsAttribute(kwargs, var_)) { + attributes.push({ + var: var_, + type: type_ + }); + } + } + + return attributes; +} + export function inArray(item: type, array: type[]) { return array.some((x) => item === x); } @@ -173,3 +274,12 @@ function parseFromBody(body: string[], pattern: RegExp): Returns | Yields { function isIterator(type: string): boolean { return type.startsWith("Generator") || type.startsWith("Iterator"); } + +function containsAttribute(attributes: Attribute[], name: string): boolean { + for (const attribute of attributes) { + if (attribute.var === name) { + return true; + } + } + return false; +} diff --git a/src/test/docstring/generate_docstring.spec.ts b/src/test/docstring/generate_docstring.spec.ts index b7c2f17..9b6c864 100644 --- a/src/test/docstring/generate_docstring.spec.ts +++ b/src/test/docstring/generate_docstring.spec.ts @@ -458,9 +458,12 @@ const defaultDocstringComponents: DocstringParts = { decorators: [], args: [], kwargs: [], + exceptions: [], returns: { type: "" }, yields: { type: "" }, - exceptions: [], + classes: [], + methods: [], + attributes: [] }; const noTypesTemplate = `{{#args}}{{var}} {{type}}{{/args}} diff --git a/src/test/integration/integration.test.ts b/src/test/integration/integration.test.ts index 3b77e9f..4805706 100644 --- a/src/test/integration/integration.test.ts +++ b/src/test/integration/integration.test.ts @@ -167,6 +167,28 @@ describe("Basic Integration Tests", function () { }); }); + it("generates class docstring by parsing __init__ in file 7", async function () { + await testDocstringGeneration({ + expectedOutputFilePath: path.resolve( + __dirname, + "./python_test_files/file_7_output.py", + ), + inputFilePath: path.resolve(__dirname, "./python_test_files/file_7.py"), + position: new vsc.Position(1, 0), + }); + }); + + it("generates module docstring by parsing document and listting classes and methods in file 8", async function () { + await testDocstringGeneration({ + expectedOutputFilePath: path.resolve( + __dirname, + "./python_test_files/file_8_output.py", + ), + inputFilePath: path.resolve(__dirname, "./python_test_files/file_8.py"), + position: new vsc.Position(0, 0), + }); + }); + it("generates a docstring for the starlark function", async function () { await testDocstringGeneration({ expectedOutputFilePath: path.resolve( diff --git a/src/test/integration/python_test_files/file_7.py b/src/test/integration/python_test_files/file_7.py new file mode 100644 index 0000000..361f6d5 --- /dev/null +++ b/src/test/integration/python_test_files/file_7.py @@ -0,0 +1,10 @@ +class TestClass(object): + + def __init__(self, arg1: int, arg2: str, arg3: float = None): + self.arg1 = arg1 + self.arg2 = arg2 + self.arg3 = arg3 + self.arg4 = self._get_arg4() + + def _get_arg4(self): + return 4 diff --git a/src/test/integration/python_test_files/file_7_output.py b/src/test/integration/python_test_files/file_7_output.py new file mode 100644 index 0000000..e41776e --- /dev/null +++ b/src/test/integration/python_test_files/file_7_output.py @@ -0,0 +1,18 @@ +class TestClass(object): + """[summary] + + :param arg1: [description] + :type arg1: int + :param arg2: [description] + :type arg2: str + :param arg3: [description], defaults to None + :type arg3: float, optional + """ + def __init__(self, arg1: int, arg2: str, arg3: float = None): + self.arg1 = arg1 + self.arg2 = arg2 + self.arg3 = arg3 + self.arg4 = self._get_arg4() + + def _get_arg4(self): + return 4 diff --git a/src/test/integration/python_test_files/file_8.py b/src/test/integration/python_test_files/file_8.py new file mode 100644 index 0000000..5239d6b --- /dev/null +++ b/src/test/integration/python_test_files/file_8.py @@ -0,0 +1,40 @@ + +from typing import Union, List, Dict, Thing, Generator, Tuple + + +class TestClass(object): + + def __init__(self, arg1: int, arg2: str, arg3: float = None): + self.arg1 = arg1 + self.arg2 = arg2 + self.arg3 = arg3 + self.arg4 = self._get_arg4() + + def _get_arg4(self): + return 4 + + +def function_1(arg1: int) -> str: # comment + + if arg1 > 1: + raise FileExistsError() # comment + + return "abc" # comment + + +def function_2(arg1, arg2, kwarg1=1): + + if arg2 > 1: + raise FileExistsError() + + yield 1 + return arg1 + + +def function_3( + arg1: int, + arg2: Union[List[str], Dict[str, int], Thing], + kwarg1: int = 1 +) -> Generator[Tuple[str, str]]: + + yield ("abc", "def") diff --git a/src/test/integration/python_test_files/file_8_output.py b/src/test/integration/python_test_files/file_8_output.py new file mode 100644 index 0000000..8328cd1 --- /dev/null +++ b/src/test/integration/python_test_files/file_8_output.py @@ -0,0 +1,51 @@ +"""[summary] + +Classes +------- +TestClass + +Methods +------- +function_1 +function_2 +function_3 +""" +from typing import Union, List, Dict, Thing, Generator, Tuple + + +class TestClass(object): + + def __init__(self, arg1: int, arg2: str, arg3: float = None): + self.arg1 = arg1 + self.arg2 = arg2 + self.arg3 = arg3 + self.arg4 = self._get_arg4() + + def _get_arg4(self): + return 4 + + +def function_1(arg1: int) -> str: # comment + + if arg1 > 1: + raise FileExistsError() # comment + + return "abc" # comment + + +def function_2(arg1, arg2, kwarg1=1): + + if arg2 > 1: + raise FileExistsError() + + yield 1 + return arg1 + + +def function_3( + arg1: int, + arg2: Union[List[str], Dict[str, int], Thing], + kwarg1: int = 1 +) -> Generator[Tuple[str, str]]: + + yield ("abc", "def") diff --git a/src/test/parse/get_body.spec.ts b/src/test/parse/get_body.spec.ts index 1d47523..c77a7e0 100644 --- a/src/test/parse/get_body.spec.ts +++ b/src/test/parse/get_body.spec.ts @@ -7,55 +7,109 @@ chai.config.truncateThreshold = 0; const expect = chai.expect; describe("getBody()", () => { - it("should return the body of a function", () => { - const result = getBody(basicFunction, 4); - - expect(result).to.have.deep.members([ - 'print("HELLO WORLD")', - "try:", - "something()", - "except Error:", - "raise SomethingWentWrong", - "return 3", - ]); - }); + context("when encoutering a function", () => { + it("should return the body of a function", () => { + const result = getBody("method", basicFunction, 4); - it("should skip blank lines", () => { - const result = getBody(gapFunction, 5); + expect(result).to.have.deep.members([ + 'print("HELLO WORLD")', + "try:", + "something()", + "except Error:", + "raise SomethingWentWrong", + "return 3", + ]); + }); - expect(result).to.have.deep.members(["print('HELLO WORLD')", "print('HELLO AGAIN')"]); - }); + it("should skip blank lines", () => { + const result = getBody("method", gapFunction, 5); - it("should skip comment lines", () => { - const result = getBody(commentFunction, 5); + expect(result).to.have.deep.members(["print('HELLO WORLD')", "print('HELLO AGAIN')"]); + }); - expect(result).to.have.deep.members(["print('HELLO AGAIN')"]); - }); + it("should skip comment lines", () => { + const result = getBody("method", commentFunction, 5); - it("should handle multi line definitions", () => { - const result = getBody(multiLineDefFunction, 4); + expect(result).to.have.deep.members(["print('HELLO AGAIN')"]); + }); - expect(result).to.have.deep.members(["pass"]); - }); + it("should handle multi line definitions", () => { + const result = getBody("method", multiLineDefFunction, 4); + + expect(result).to.have.deep.members(["pass"]); + }); + + it("should handle indented functions", () => { + const result = getBody("method", indentedFunctions, 3); + + expect(result).to.have.deep.members(["return 2"]); + + const result2 = getBody("method", indentedFunctions, 6); - it("should handle indented functions", () => { - const result = getBody(indentedFunctions, 3); + expect(result2).to.have.deep.members(["pass"]); + }); - expect(result).to.have.deep.members(["return 2"]); + it("should return an empty array if a function has no body", () => { + const result = getBody("method", noBody, 2); - const result2 = getBody(indentedFunctions, 6); + expect(result).to.have.deep.members([]); - expect(result2).to.have.deep.members(["pass"]); + const result2 = getBody("method", noBody, 4); + + expect(result2).to.have.deep.members([]); + }); }); + context("when encoutering a class", () => { + it("should return the body of a function", () => { + const result = getBody("method", basicFunction, 4); + + expect(result).to.have.deep.members([ + 'print("HELLO WORLD")', + "try:", + "something()", + "except Error:", + "raise SomethingWentWrong", + "return 3", + ]); + }); + + it("should skip blank lines", () => { + const result = getBody("method", gapFunction, 5); + + expect(result).to.have.deep.members(["print('HELLO WORLD')", "print('HELLO AGAIN')"]); + }); + + it("should skip comment lines", () => { + const result = getBody("method", commentFunction, 5); + + expect(result).to.have.deep.members(["print('HELLO AGAIN')"]); + }); + + it("should handle multi line definitions", () => { + const result = getBody("method", multiLineDefFunction, 4); + + expect(result).to.have.deep.members(["pass"]); + }); - it("should return an empty array if a function has no body", () => { - const result = getBody(noBody, 2); + it("should handle indented functions", () => { + const result = getBody("method", indentedFunctions, 3); - expect(result).to.have.deep.members([]); + expect(result).to.have.deep.members(["return 2"]); - const result2 = getBody(noBody, 4); + const result2 = getBody("method", indentedFunctions, 6); - expect(result2).to.have.deep.members([]); + expect(result2).to.have.deep.members(["pass"]); + }); + + it("should return an empty array if a function has no body", () => { + const result = getBody("method", noBody, 2); + + expect(result).to.have.deep.members([]); + + const result2 = getBody("method", noBody, 4); + + expect(result2).to.have.deep.members([]); + }); }); }); @@ -123,7 +177,31 @@ def next_func(): const noBody = ` def no_body(): - + def next_no_body(): `; + +const basicClass = ` +Something Else + +class BasicClass(object): + + def __init__(self, param1): + self.param1 = param1 + + def hello(self): + print("Hello world") + +class AnotherBasicClass(object): + + def __init__( + self, param2 + ): + self.param2 = param2 + + def hello(self): + print("Goodbye world") +`; + +const docstringType = 'method'; diff --git a/src/test/parse/get_definition.spec.ts b/src/test/parse/get_definition.spec.ts index 7c4f3cb..10d73e8 100644 --- a/src/test/parse/get_definition.spec.ts +++ b/src/test/parse/get_definition.spec.ts @@ -26,7 +26,7 @@ describe("getDefinition()", () => { expect(result).to.equal("def multi_line_function( param1, param2 = 1):"); }); - it("should get ignore commented lines in a multiline function definition", () => { + it("should ignore commented lines in a multiline function definition", () => { const result = getDefinition(multiLineCommentedLineFunction, 9); expect(result).to.equal( @@ -60,10 +60,15 @@ describe("getDefinition()", () => { }); context("when encountering a class", () => { - it("should get the class definition", () => { + it("should get the class definition from init", () => { const result = getDefinition(basicClass, 4); - expect(result).to.equal("class BasicClass(object):"); + expect(result).to.equal("class BasicClass(self, param1):"); + }); + it("should get the class definition from multiline init", () => { + const result = getDefinition(basicClass, 12); + + expect(result).to.equal("class AnotherBasicClass(self, param2):"); }); }); }); @@ -95,8 +100,8 @@ Something Else const multiLineFunction = ` Something Else -def multi_line_function( - param1, +def multi_line_function( + param1, param2 = 1): print("HELLO WORLD") @@ -158,4 +163,14 @@ class BasicClass(object): def hello(self): print("Hello world") + +class AnotherBasicClass(object): + + def __init__( + self, param2 + ): + self.param2 = param2 + + def hello(self): + print("Goodbye world") `; diff --git a/src/test/parse/get_docstring_indentation.spec.ts b/src/test/parse/get_docstring_indentation.spec.ts index 8539d00..1bf0f22 100644 --- a/src/test/parse/get_docstring_indentation.spec.ts +++ b/src/test/parse/get_docstring_indentation.spec.ts @@ -43,11 +43,18 @@ describe("getDocstringIndentation()", () => { expect(result).to.equal("\t\t"); }); - it("should default to default indentation if no indentation is found", () => { - const result = getDocstringIndentation("", 0, " ".repeat(4)); + it("should default to default indentation if no indentation is found and line position is not 0", () => { + const result = getDocstringIndentation(" \n \n", 1, " ".repeat(4)); expect(result).to.equal(" ".repeat(4)); }); + + it("should default to no space if no indentation is found and line position is 0", () => { + const result = getDocstringIndentation("", 0, " ".repeat(4)); + + expect(result).to.equal(""); + }); + }); const fourSpaceIndentation = ` diff --git a/src/test/parse/parse_parameters.spec.ts b/src/test/parse/parse_parameters.spec.ts index 2b32d0c..4f879cc 100644 --- a/src/test/parse/parse_parameters.spec.ts +++ b/src/test/parse/parse_parameters.spec.ts @@ -7,64 +7,157 @@ chai.config.truncateThreshold = 0; const expect = chai.expect; describe("parseParameters()", () => { - it("should parse an array of strings into a docstring struct", () => { - const parameterTokens = [ - "@decorator1", - "@decorator2", - "param1", - "param2: int", - "param3 = 1", - "param4: str = 'abc'", - "-> int", - ]; + context("when encoutering a method", () => { + it("should parse an array of strings into a docstring struct", () => { + const parameterTokens = [ + "@decorator1", + "@decorator2", + "param1", + "param2: int", + "param3 = 1", + "param4: str = 'abc'", + "-> int", + ]; + + const body = [" raise Exception", "raise Exception2"]; + + const functionName = "function"; + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, body, functionName); + + expect(result).to.eql({ + name: "function", + decorators: [{ name: "decorator1" }, { name: "decorator2" }], + args: [ + { var: "param1", type: undefined }, + { var: "param2", type: "int" }, + ], + kwargs: [ + { var: "param3", default: "1", type: "int" }, + { var: "param4", default: "'abc'", type: "str" }, + ], + returns: { type: "int" }, + yields: undefined, + exceptions: [{ type: "Exception" }, { type: "Exception2" }], + classes: [], + methods: [], + attributes: [], + }); + }); - const body = [" raise Exception", "raise Exception2"]; + it("should parse args with and without type hints", () => { + const parameterTokens = ["param1: List[string]", "param2"]; + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); - const functionName = "function"; + expect(result.args).to.have.deep.members([ + { var: "param1", type: "List[string]" }, + { var: "param2", type: undefined }, + ]); + }); - const result = parseParameters(parameterTokens, body, functionName); + it("should parse kwargs with and without type hints", () => { + const parameterTokens = ["param1: List[int] = [1,2]", "param2 = 'abc'"]; + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); - expect(result).to.eql({ - name: "function", - decorators: [{ name: "decorator1" }, { name: "decorator2" }], - args: [ - { var: "param1", type: undefined }, - { var: "param2", type: "int" }, - ], - kwargs: [ - { var: "param3", default: "1", type: "int" }, - { var: "param4", default: "'abc'", type: "str" }, - ], - returns: { type: "int" }, - yields: undefined, - exceptions: [{ type: "Exception" }, { type: "Exception2" }], + expect(result.kwargs).to.have.deep.members([ + { var: "param1", default: "[1,2]", type: "List[int]" }, + { var: "param2", default: "'abc'", type: "str" }, + ]); }); }); - it("should parse args with and without type hints", () => { - const parameterTokens = ["param1: List[string]", "param2"]; - const result = parseParameters(parameterTokens, [], "name"); - - expect(result.args).to.have.deep.members([ - { var: "param1", type: "List[string]" }, - { var: "param2", type: undefined }, - ]); - }); + context("when encoutering a class", () => { + it("should return only parameters when no attributes are defined in body", () => { + const parameterTokens = [ + "@decorator1", + "@decorator2", + "param1", + "param2: int", + "param3 = 1", + "param4: str = 'abc'", + "-> int", + ]; + + const body = [" raise Exception", "raise Exception2"]; + + const functionName = "testClass"; + const docstringType = "class"; + const result = parseParameters(docstringType, parameterTokens, body, functionName); + + expect(result).to.eql({ + name: "testClass", + decorators: [{ name: "decorator1" }, { name: "decorator2" }], + args: [ + { var: "param1", type: undefined }, + { var: "param2", type: "int" }, + ], + kwargs: [ + { var: "param3", default: "1", type: "int" }, + { var: "param4", default: "'abc'", type: "str" }, + ], + returns: undefined, + yields: undefined, + exceptions: [], + classes: [], + methods: [], + attributes: [], + }); + }); - it("should parse kwargs with and without type hints", () => { - const parameterTokens = ["param1: List[int] = [1,2]", "param2 = 'abc'"]; - const result = parseParameters(parameterTokens, [], "name"); + it("should return only parameters when no attributes are defined in body", () => { + const parameterTokens = [ + "@decorator1", + "@decorator2", + "param1", + "param2: int", + "param3 = 1", + "param4: str = 'abc'", + "-> int", + ]; + + const body = ["", + " def __init__(self, param1, param2: int, param3 = 1, param4: str = 'abc'):", + " self.param1 = param1", + " self.param2 = param2", + " self.param3 = param3", + " self.param4 = param4", + " self.param5 = 7"]; + + const functionName = "testClass"; + const docstringType = "class"; + const result = parseParameters(docstringType, parameterTokens, body, functionName); + + expect(result).to.eql({ + name: "testClass", + decorators: [{ name: "decorator1" }, { name: "decorator2" }], + args: [ + { var: "param1", type: undefined }, + { var: "param2", type: "int" }, + ], + kwargs: [ + { var: "param3", default: "1", type: "int" }, + { var: "param4", default: "'abc'", type: "str" }, + ], + returns: undefined, + yields: undefined, + exceptions: [], + classes: [], + methods: [], + attributes: [ + { var: "param5", type: undefined} + ], + }); + }); - expect(result.kwargs).to.have.deep.members([ - { var: "param1", default: "[1,2]", type: "List[int]" }, - { var: "param2", default: "'abc'", type: "str" }, - ]); }); describe("parseReturns", () => { it("should parse return types", () => { const parameterTokens = ["-> List[int]"]; - const result = parseParameters(parameterTokens, [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); expect(result.returns).to.deep.equal({ type: "List[int]", @@ -73,21 +166,24 @@ describe("parseParameters()", () => { it("should not parse '-> None' return types", () => { const parameterTokens = ["-> None"]; - const result = parseParameters(parameterTokens, [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); expect(result.returns).to.deep.equal(undefined); }); it("should not parse '-> Generator' return types", () => { const parameterTokens = ["-> Generator[int]"]; - const result = parseParameters(parameterTokens, [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); expect(result.returns).to.deep.equal(undefined); }); it("should not parse '-> Iterator' return types", () => { const parameterTokens = ["-> Iterator[int]"]; - const result = parseParameters(parameterTokens, [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); expect(result.returns).to.deep.equal(undefined); }); @@ -97,7 +193,8 @@ describe("parseParameters()", () => { it("should use the signature return type if it is an Iterator", () => { const parameterTokens = ["-> Iterator[int]"]; const body = []; - const result = parseParameters(parameterTokens, body, "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, body, "name"); expect(result.yields).to.deep.equal({ type: "Iterator[int]", @@ -107,7 +204,8 @@ describe("parseParameters()", () => { it("should use the signature return type if it is an Generator", () => { const parameterTokens = ["-> Generator[int]"]; const body = []; - const result = parseParameters(parameterTokens, body, "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, body, "name"); expect(result.yields).to.deep.equal({ type: "Generator[int]", @@ -117,7 +215,8 @@ describe("parseParameters()", () => { it("Should use the return type as the yield type if a yield exists in the body", () => { const parameterTokens = ["-> int"]; const body = ["yield 4"]; - const result = parseParameters(parameterTokens, body, "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, body, "name"); expect(result.yields).to.eql({ type: "Iterator[int]", @@ -127,7 +226,8 @@ describe("parseParameters()", () => { it("Should return a yield without type if a yield exists in the body but there is no return signature", () => { const parameterTokens = [""]; const body = ["yield 4"]; - const result = parseParameters(parameterTokens, body, "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, body, "name"); expect(result.yields).to.eql({ type: undefined, @@ -137,14 +237,16 @@ describe("parseParameters()", () => { it("Should return undefined if no yield exists in the signature or body", () => { const parameterTokens = ["-> List[int]"]; const body = []; - const result = parseParameters(parameterTokens, body, "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, body, "name"); expect(result.yields).to.eql(undefined); }); }); it("should result in no yield if there is no yield type or yield in body", () => { - const result = parseParameters([], [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, [], [], "name"); expect(result.returns).to.eql(undefined); }); @@ -152,7 +254,8 @@ describe("parseParameters()", () => { it("should parse the return from the body if there is no return type in the definition", () => { const parameterTokens = ["param1"]; const body = ["return 3"]; - const result = parseParameters(parameterTokens, body, ""); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, body, ""); expect(result.returns).to.eql({ type: undefined, @@ -160,14 +263,16 @@ describe("parseParameters()", () => { }); it("should result in no return if there is no return type or return in body", () => { - const result = parseParameters([], [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, [], [], "name"); expect(result.returns).to.eql(undefined); }); it("should parse simple exception", () => { + const docstringType = "method"; const functionContent = ["raise Exception"]; - const result = parseParameters([], functionContent, ""); + const result = parseParameters(docstringType, [], functionContent, ""); expect(result.exceptions).to.have.deep.members([{ type: "Exception" }]); }); @@ -183,7 +288,8 @@ describe("parseParameters()", () => { " raise RiskyException", "raise AlwaysCrapsOut", ]; - const result = parseParameters([], functionContent, ""); + const docstringType = "method"; + const result = parseParameters(docstringType, [], functionContent, ""); expect(result.exceptions).to.have.deep.members([ { type: "BadVar" }, @@ -194,15 +300,16 @@ describe("parseParameters()", () => { it("should not parse exception after inline comment", () => { const functionContent = ["arg1 + arg2 # todo: raise an error"]; - const result = parseParameters([], functionContent, ""); + const result = parseParameters("method", [], functionContent, ""); expect(result.exceptions).to.eql([]); }); context("when the parameters have strange spacing", () => { it("should parse args with strange spacing", () => { + const docstringType = "method"; const parameterTokens = [" param1 : int ", " param2 ", "param3:List[int]"]; - const result = parseParameters(parameterTokens, [], "name"); + const result = parseParameters(docstringType, parameterTokens, [], "name"); expect(result.args).to.have.deep.members([ { var: "param1", type: "int" }, @@ -213,7 +320,8 @@ describe("parseParameters()", () => { it("should parse kwargs with strange spacing", () => { const parameterTokens = [" param1 : str\t=\t'abc'", " param2 = 1", "param3:int=2"]; - const result = parseParameters(parameterTokens, [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); expect(result.kwargs).to.have.deep.members([ { var: "param1", default: "'abc'", type: "str" }, @@ -224,7 +332,8 @@ describe("parseParameters()", () => { it("should parse return types with strange spacing", () => { const parameterTokens = ["\t -> \tint \t"]; - const result = parseParameters(parameterTokens, [], "name"); + const docstringType = "method"; + const result = parseParameters(docstringType, parameterTokens, [], "name"); expect(result.returns).to.deep.equal({ type: "int", diff --git a/src/test/run_integration_tests.js b/src/test/run_integration_tests.js new file mode 100644 index 0000000..7203b54 --- /dev/null +++ b/src/test/run_integration_tests.js @@ -0,0 +1,75 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +var path = require("path"); +var vscode_test_1 = require("vscode-test"); +function main() { + return __awaiter(this, void 0, void 0, function () { + var vscodeVersion, extensionDevelopmentPath, extensionTestsPath, err_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + console.log("Running Integration tests"); + vscodeVersion = process.env.VSCODE_VERSION || "insiders"; + console.log("Using vscode version:", vscodeVersion); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + extensionDevelopmentPath = path.resolve(__dirname, "../../"); + extensionTestsPath = path.resolve(__dirname, "./integration/index"); + return [4 /*yield*/, vscode_test_1.runTests({ + version: vscodeVersion, + extensionDevelopmentPath: extensionDevelopmentPath, + extensionTestsPath: extensionTestsPath, + launchArgs: ["--disable-extensions"] + })]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _a.sent(); + console.error(err_1); + console.error("Failed to run tests"); + process.exit(1); + return [3 /*break*/, 4]; + case 4: return [2 /*return*/]; + } + }); + }); +} +main(); diff --git a/src/test/run_integration_tests.ts b/src/test/run_integration_tests.ts index 699c415..e6f09c0 100644 --- a/src/test/run_integration_tests.ts +++ b/src/test/run_integration_tests.ts @@ -1,3 +1,4 @@ + import * as path from "path"; import { runTests } from "vscode-test"; @@ -17,6 +18,7 @@ async function main() { launchArgs: ["--disable-extensions"], }); } catch (err) { + console.error(err) console.error("Failed to run tests"); process.exit(1); }