From d1da48b6f5a02203afcb4a4c90eea31c77aaeaf6 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 3 Oct 2017 14:29:33 -0700 Subject: [PATCH 01/57] update to use latest api --- .gitignore | 2 ++ .vscode/launch.json | 9 ++++++--- package.json | 14 +++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 81f31ac0a202..58ce4158ddbe 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ node_modules src/test/.vscode/** **/testFiles/**/.cache/** *.noseids +.vscode-test +__pycache__ diff --git a/.vscode/launch.json b/.vscode/launch.json index 0d64d8b63590..284cb90bbaa5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -30,7 +30,9 @@ ], "stopOnEntry": false, "sourceMaps": true, - "outDir": "${workspaceRoot}/out", + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ], "preLaunchTask": "npm" }, { @@ -43,7 +45,9 @@ "--server=4711" ], "sourceMaps": true, - "outDir": "${workspaceRoot}/out/client", + "outFiles": [ + "${workspaceRoot}/out/client/**/*.js" + ], "cwd": "${workspaceRoot}" }, { @@ -58,7 +62,6 @@ ], "stopOnEntry": false, "sourceMaps": true, - "xxoutDir": "${workspaceRoot}/out/test", "outFiles": [ "${workspaceRoot}/out/**/*.js" ], diff --git a/package.json b/package.json index 3307ab32c2ed..bb660b4206c0 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "theme": "dark" }, "engines": { - "vscode": "^1.9.0" + "vscode": "^1.15.0" }, "recommendations": [ "donjayamanne.jupyter" @@ -1537,11 +1537,12 @@ "untildify": "^3.0.2", "vscode-debugadapter": "^1.0.1", "vscode-debugprotocol": "^1.0.1", - "vscode-extension-telemetry": "0.0.5", - "vscode-languageclient": "^1.1.0", - "vscode-languageserver": "^1.1.0", + "vscode-extension-telemetry": "^0.0.5", + "vscode-languageclient": "^3.1.0", + "vscode-languageserver": "^3.1.0", "winreg": "^1.2.4", - "xml2js": "^0.4.17" + "xml2js": "^0.4.17", + "vscode": "^1.0.3" }, "devDependencies": { "@types/fs-extra": "^4.0.2", @@ -1560,13 +1561,12 @@ "babel-loader": "^6.2.5", "babel-preset-es2015": "^6.14.0", "ignore-loader": "^0.1.1", - "mocha": "^2.3.3", + "mocha": "^3.2.0", "retyped-diff-match-patch-tsd-ambient": "^1.0.0-0", "sinon": "^2.3.6", "ts-loader": "^0.8.2", "tslint": "^3.15.1", "typescript": "^2.3.2", - "vscode": "^1.0.3", "webpack": "^1.13.2" } } From b305a72e0601ae1a9e3a30680fac6a2803dc36af Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 5 Oct 2017 15:47:14 -0700 Subject: [PATCH 02/57] config changes for multiroot workspace --- package.json | 243 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 162 insertions(+), 81 deletions(-) diff --git a/package.json b/package.json index d027214ce152..e19878709162 100644 --- a/package.json +++ b/package.json @@ -922,32 +922,38 @@ "python.promptToInstallJupyter": { "type": "boolean", "default": true, - "description": "Display prompt to install Jupyter Extension." + "description": "Display prompt to install Jupyter Extension.", + "scope": "resource" }, "python.pythonPath": { "type": "string", "default": "python", - "description": "Path to Python, you can use a custom version of Python by modifying this setting to include the full path." + "description": "Path to Python, you can use a custom version of Python by modifying this setting to include the full path.", + "scope": "resource" }, "python.venvPath": { "type": "string", "default": "", - "description": "Path to folder with a list of Virtual Environments (e.g. ~/.pyenv, ~/Envs, ~/.virtualenvs)." + "description": "Path to folder with a list of Virtual Environments (e.g. ~/.pyenv, ~/Envs, ~/.virtualenvs).", + "scope": "resource" }, "python.envFile": { "type": "string", "description": "Absolute path to a file containing environment variable definitions.", - "default": "${workspaceRoot}/.env" + "default": "${workspaceRoot}/.env", + "scope": "resource" }, "python.jediPath": { "type": "string", "default": "", - "description": "Path to directory containing the Jedi library (this path will contain the 'Jedi' sub directory)." + "description": "Path to directory containing the Jedi library (this path will contain the 'Jedi' sub directory).", + "scope": "resource" }, "python.sortImports.path": { "type": "string", "description": "Path to isort script, default using inner version", - "default": "" + "default": "", + "scope": "resource" }, "python.sortImports.args": { "type": "array", @@ -955,7 +961,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.disablePromptForFeatures": { "type": "array", @@ -974,62 +981,74 @@ "pydocstyle", "pylint" ] - } + }, + "scope": "resource" }, "python.linting.enabled": { "type": "boolean", "default": true, - "description": "Whether to lint Python files." + "description": "Whether to lint Python files.", + "scope": "resource" }, "python.linting.enabledWithoutWorkspace": { "type": "boolean", "default": true, - "description": "Whether to lint Python files when no workspace is opened." + "description": "Whether to lint Python files when no workspace is opened.", + "scope": "resource" }, "python.linting.prospectorEnabled": { "type": "boolean", "default": false, - "description": "Whether to lint Python files using prospector." + "description": "Whether to lint Python files using prospector.", + "scope": "resource" }, "python.linting.pylintEnabled": { "type": "boolean", "default": true, - "description": "Whether to lint Python files using pylint." + "description": "Whether to lint Python files using pylint.", + "scope": "resource" }, "python.linting.pep8Enabled": { "type": "boolean", "default": false, - "description": "Whether to lint Python files using pep8" + "description": "Whether to lint Python files using pep8", + "scope": "resource" }, "python.linting.flake8Enabled": { "type": "boolean", "default": false, - "description": "Whether to lint Python files using flake8" + "description": "Whether to lint Python files using flake8", + "scope": "resource" }, "python.linting.pydocstyleEnabled": { "type": "boolean", "default": false, - "description": "Whether to lint Python files using pydocstyle" + "description": "Whether to lint Python files using pydocstyle", + "scope": "resource" }, "python.linting.mypyEnabled": { "type": "boolean", "default": false, - "description": "Whether to lint Python files using mypy." + "description": "Whether to lint Python files using mypy.", + "scope": "resource" }, "python.linting.lintOnTextChange": { "type": "boolean", "default": true, - "description": "Whether to lint Python files when modified." + "description": "Whether to lint Python files when modified.", + "scope": "resource" }, "python.linting.lintOnSave": { "type": "boolean", "default": true, - "description": "Whether to lint Python files when saved." + "description": "Whether to lint Python files when saved.", + "scope": "resource" }, "python.linting.maxNumberOfProblems": { "type": "number", "default": 100, - "description": "Controls the maximum number of problems produced by the server." + "description": "Controls the maximum number of problems produced by the server.", + "scope": "resource" }, "python.linting.pylintCategorySeverity.convention": { "type": "string", @@ -1040,7 +1059,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.pylintCategorySeverity.refactor": { "type": "string", @@ -1051,7 +1071,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.pylintCategorySeverity.warning": { "type": "string", @@ -1062,7 +1083,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.pylintCategorySeverity.error": { "type": "string", @@ -1073,7 +1095,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.pylintCategorySeverity.fatal": { "type": "string", @@ -1084,7 +1107,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.pep8CategorySeverity.W": { "type": "string", @@ -1095,7 +1119,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.pep8CategorySeverity.E": { "type": "string", @@ -1106,7 +1131,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.flake8CategorySeverity.F": { "type": "string", @@ -1117,7 +1143,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.flake8CategorySeverity.E": { "type": "string", @@ -1128,7 +1155,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.flake8CategorySeverity.W": { "type": "string", @@ -1139,7 +1167,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.mypyCategorySeverity.error": { "type": "string", @@ -1150,7 +1179,8 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.mypyCategorySeverity.note": { "type": "string", @@ -1161,37 +1191,44 @@ "Error", "Information", "Warning" - ] + ], + "scope": "resource" }, "python.linting.prospectorPath": { "type": "string", "default": "prospector", - "description": "Path to Prospector, you can use a custom version of prospector by modifying this setting to include the full path." + "description": "Path to Prospector, you can use a custom version of prospector by modifying this setting to include the full path.", + "scope": "resource" }, "python.linting.pylintPath": { "type": "string", "default": "pylint", - "description": "Path to Pylint, you can use a custom version of pylint by modifying this setting to include the full path." + "description": "Path to Pylint, you can use a custom version of pylint by modifying this setting to include the full path.", + "scope": "resource" }, "python.linting.pep8Path": { "type": "string", "default": "pep8", - "description": "Path to pep8, you can use a custom version of pep8 by modifying this setting to include the full path." + "description": "Path to pep8, you can use a custom version of pep8 by modifying this setting to include the full path.", + "scope": "resource" }, "python.linting.flake8Path": { "type": "string", "default": "flake8", - "description": "Path to flake8, you can use a custom version of flake8 by modifying this setting to include the full path." + "description": "Path to flake8, you can use a custom version of flake8 by modifying this setting to include the full path.", + "scope": "resource" }, "python.linting.pydocstylePath": { "type": "string", "default": "pydocstyle", - "description": "Path to pydocstyle, you can use a custom version of pydocstyle by modifying this setting to include the full path." + "description": "Path to pydocstyle, you can use a custom version of pydocstyle by modifying this setting to include the full path.", + "scope": "resource" }, "python.linting.mypyPath": { "type": "string", "default": "mypy", - "description": "Path to mypy, you can use a custom version of mypy by modifying this setting to include the full path." + "description": "Path to mypy, you can use a custom version of mypy by modifying this setting to include the full path.", + "scope": "resource" }, "python.linting.prospectorArgs": { "type": "array", @@ -1199,7 +1236,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.pylintArgs": { "type": "array", @@ -1207,7 +1245,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.pep8Args": { "type": "array", @@ -1215,7 +1254,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.flake8Args": { "type": "array", @@ -1223,7 +1263,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.pydocstyleArgs": { "type": "array", @@ -1231,7 +1272,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.mypyArgs": { "type": "array", @@ -1242,12 +1284,14 @@ ], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.outputWindow": { "type": "string", "default": "Python", - "description": "The output window name for the linting messages, defaults to Python output window." + "description": "The output window name for the linting messages, defaults to Python output window.", + "scope": "resource" }, "python.formatting.provider": { "type": "string", @@ -1257,17 +1301,20 @@ "autopep8", "yapf", "none" - ] + ], + "scope": "resource" }, "python.formatting.autopep8Path": { "type": "string", "default": "autopep8", - "description": "Path to autopep8, you can use a custom version of autopep8 by modifying this setting to include the full path." + "description": "Path to autopep8, you can use a custom version of autopep8 by modifying this setting to include the full path.", + "scope": "resource" }, "python.formatting.yapfPath": { "type": "string", "default": "yapf", - "description": "Path to yapf, you can use a custom version of yapf by modifying this setting to include the full path." + "description": "Path to yapf, you can use a custom version of yapf by modifying this setting to include the full path.", + "scope": "resource" }, "python.formatting.autopep8Args": { "type": "array", @@ -1275,7 +1322,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.formatting.yapfArgs": { "type": "array", @@ -1283,17 +1331,20 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.formatting.formatOnSave": { "type": "boolean", "default": false, - "description": "Format the document upon saving." + "description": "Format the document upon saving.", + "scope": "resource" }, "python.formatting.outputWindow": { "type": "string", "default": "Python", - "description": "The output window name for the formatting messages, defaults to Python output window." + "description": "The output window name for the formatting messages, defaults to Python output window.", + "scope": "resource" }, "python.autoComplete.preloadModules": { "type": "array", @@ -1301,42 +1352,50 @@ "type": "string" }, "default": [], - "description": "Comma delimited list of modules preloaded to speed up Auto Complete (e.g. add Numpy, Pandas, etc, items slow to load when autocompleting)." + "description": "Comma delimited list of modules preloaded to speed up Auto Complete (e.g. add Numpy, Pandas, etc, items slow to load when autocompleting).", + "scope": "resource" }, "python.autoComplete.extraPaths": { "type": "array", "default": [], - "description": "List of paths to libraries and the like that need to be imported by auto complete engine. E.g. when using Google App SDK, the paths are not in system path, hence need to be added into this list." + "description": "List of paths to libraries and the like that need to be imported by auto complete engine. E.g. when using Google App SDK, the paths are not in system path, hence need to be added into this list.", + "scope": "resource" }, "python.autoComplete.addBrackets": { "type": "boolean", "default": false, - "description": "Automatically add brackets for functions." + "description": "Automatically add brackets for functions.", + "scope": "resource" }, "python.workspaceSymbols.tagFilePath": { "type": "string", "default": "${workspaceRoot}/.vscode/tags", - "description": "Fully qualified path to tag file (exuberant ctag file), used to provide workspace symbols." + "description": "Fully qualified path to tag file (exuberant ctag file), used to provide workspace symbols.", + "scope": "resource" }, "python.workspaceSymbols.enabled": { "type": "boolean", "default": true, - "description": "Set to 'false' to disable Workspace Symbol provider using ctags." + "description": "Set to 'false' to disable Workspace Symbol provider using ctags.", + "scope": "resource" }, "python.workspaceSymbols.rebuildOnStart": { "type": "boolean", "default": true, - "description": "Whether to re-build the tags file on start (defaults to true)." + "description": "Whether to re-build the tags file on start (defaults to true).", + "scope": "resource" }, "python.workspaceSymbols.rebuildOnFileSave": { "type": "boolean", "default": true, - "description": "Whether to re-build the tags file on when changes made to python files are saved." + "description": "Whether to re-build the tags file on when changes made to python files are saved.", + "scope": "resource" }, "python.workspaceSymbols.ctagsPath": { "type": "string", "default": "ctags", - "description": "Fully qualilified path to the ctags executable (else leave as ctags, assuming it is in current path)." + "description": "Fully qualilified path to the ctags executable (else leave as ctags, assuming it is in current path).", + "scope": "resource" }, "python.workspaceSymbols.exclusionPatterns": { "type": "array", @@ -1346,42 +1405,50 @@ "items": { "type": "string" }, - "description": "Pattern used to exclude files and folders from ctags See http://ctags.sourceforge.net/ctags.html." + "description": "Pattern used to exclude files and folders from ctags See http://ctags.sourceforge.net/ctags.html.", + "scope": "resource" }, "python.unitTest.promptToConfigure": { "type": "boolean", "default": true, - "description": "Where to prompt to configure a test framework if potential tests directories are discovered." + "description": "Where to prompt to configure a test framework if potential tests directories are discovered.", + "scope": "resource" }, "python.unitTest.debugPort": { "type": "number", "default": 3000, - "description": "Port number used for debugging of unittests." + "description": "Port number used for debugging of unittests.", + "scope": "resource" }, "python.unitTest.cwd": { "type": "string", "default": null, - "description": "Optional working directory for unit tests." + "description": "Optional working directory for unit tests.", + "scope": "resource" }, "python.unitTest.nosetestsEnabled": { "type": "boolean", "default": false, - "description": "Whether to enable or disable unit testing using nosetests." + "description": "Whether to enable or disable unit testing using nosetests.", + "scope": "resource" }, "python.unitTest.nosetestPath": { "type": "string", "default": "nosetests", - "description": "Path to nosetests, you can use a custom version of nosetests by modifying this setting to include the full path." + "description": "Path to nosetests, you can use a custom version of nosetests by modifying this setting to include the full path.", + "scope": "resource" }, "python.unitTest.pyTestEnabled": { "type": "boolean", "default": false, - "description": "Whether to enable or disable unit testing using pytest." + "description": "Whether to enable or disable unit testing using pytest.", + "scope": "resource" }, "python.unitTest.pyTestPath": { "type": "string", "default": "py.test", - "description": "Path to pytest (py.test), you can use a custom version of pytest by modifying this setting to include the full path." + "description": "Path to pytest (py.test), you can use a custom version of pytest by modifying this setting to include the full path.", + "scope": "resource" }, "python.unitTest.nosetestArgs": { "type": "array", @@ -1389,7 +1456,8 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.unitTest.pyTestArgs": { "type": "array", @@ -1397,12 +1465,14 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.unitTest.unittestEnabled": { "type": "boolean", "default": false, - "description": "Whether to enable or disable unit testing using unittest." + "description": "Whether to enable or disable unit testing using unittest.", + "scope": "resource" }, "python.unitTest.unittestArgs": { "type": "array", @@ -1416,7 +1486,8 @@ ], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.ignorePatterns": { "type": "array", @@ -1427,17 +1498,20 @@ ], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.linting.pylamaEnabled": { "type": "boolean", "default": false, - "description": "Whether to lint Python files using pylama." + "description": "Whether to lint Python files using pylama.", + "scope": "resource" }, "python.linting.pylamaPath": { "type": "string", "default": "pylama", - "description": "Path to pylama, you can use a custom version of pylama by modifying this setting to include the full path." + "description": "Path to pylama, you can use a custom version of pylama by modifying this setting to include the full path.", + "scope": "resource" }, "python.linting.pylamaArgs": { "type": "array", @@ -1445,32 +1519,38 @@ "default": [], "items": { "type": "string" - } + }, + "scope": "resource" }, "python.unitTest.outputWindow": { "type": "string", "default": "Python Test Log", - "description": "The output window name for the unit test messages, defaults to Python output window." + "description": "The output window name for the unit test messages, defaults to Python output window.", + "scope": "resource" }, "python.terminal.executeInFileDir": { "type": "boolean", "default": false, - "description": "When executing a file in the terminal, whether to use execute in the file's directory, instead of the current open folder." + "description": "When executing a file in the terminal, whether to use execute in the file's directory, instead of the current open folder.", + "scope": "resource" }, "python.terminal.launchArgs": { "type": "array", "default": [], - "description": "Python launch arguments to use when executing a file in the terminal." + "description": "Python launch arguments to use when executing a file in the terminal.", + "scope": "resource" }, "python.jupyter.appendResults": { "type": "boolean", "default": true, - "description": "Whether to appen the results to results window, else clear and display." + "description": "Whether to appen the results to results window, else clear and display.", + "scope": "resource" }, "python.jupyter.defaultKernel": { "type": "string", "default": "", - "description": "Default kernel to be used. By default the first available kernel is used." + "description": "Default kernel to be used. By default the first available kernel is used.", + "scope": "resource" }, "python.jupyter.startupCode": { "type": "array", @@ -1480,7 +1560,8 @@ "default": [ "%matplotlib inline" ], - "description": "Code executed when the kernel starts. Such as the default of '%matplotlib inline'. Individual lines can be placed in separate items of the array." + "description": "Code executed when the kernel starts. Such as the default of '%matplotlib inline'. Individual lines can be placed in separate items of the array.", + "scope": "resource" } } }, From 674da9eefa0d1a93c281c82a6f614303ee523f3c Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 5 Oct 2017 15:48:06 -0700 Subject: [PATCH 03/57] linting support with multi roots --- src/client/linters/baseLinter.ts | 10 +++++----- src/client/linters/flake8.ts | 6 +++--- src/client/linters/main.ts | 18 +++++++++--------- src/client/linters/mypy.ts | 8 ++++---- src/client/linters/pep8Linter.ts | 6 +++--- src/client/linters/prospector.ts | 6 +++--- src/client/linters/pydocstyle.ts | 6 +++--- src/client/linters/pylama.ts | 6 +++--- src/client/linters/pylint.ts | 8 ++++---- src/client/providers/lintProvider.ts | 6 ++++-- src/test/linters/lint.test.ts | 26 +++++++++++++------------- 11 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts index f4c80314c1f2..b4cd6ca154ed 100644 --- a/src/client/linters/baseLinter.ts +++ b/src/client/linters/baseLinter.ts @@ -50,15 +50,15 @@ export function matchNamedRegEx(data, regex): IRegexGroup { export abstract class BaseLinter { public Id: string; protected pythonSettings: settings.IPythonSettings; - private _workspaceRootPath: string; protected _columnOffset = 0; private _errorHandler: ErrorHandler; - protected get workspaceRootPath(): string { - return typeof this._workspaceRootPath === 'string' ? this._workspaceRootPath : vscode.workspace.rootPath; + protected getWorkspaceRootPath(document: vscode.TextDocument): string { + const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); + const workspaceRootPath = (workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string') ? workspaceFolder.uri.fsPath : undefined; + return typeof workspaceRootPath === 'string' ? workspaceRootPath : __dirname; } - constructor(id: string, public product: Product, protected outputChannel: OutputChannel, workspaceRootPath: string) { + constructor(id: string, public product: Product, protected outputChannel: OutputChannel) { this.Id = id; - this._workspaceRootPath = workspaceRootPath; this.pythonSettings = settings.PythonSettings.getInstance(); this._errorHandler = new ErrorHandler(this.Id, product, new Installer(), this.outputChannel); } diff --git a/src/client/linters/flake8.ts b/src/client/linters/flake8.ts index e2d574c63d88..b141b3a3a86c 100644 --- a/src/client/linters/flake8.ts +++ b/src/client/linters/flake8.ts @@ -8,8 +8,8 @@ import { TextDocument, CancellationToken } from 'vscode'; export class Linter extends baseLinter.BaseLinter { _columnOffset = 1; - constructor(outputChannel: OutputChannel, workspaceRootPath?: string) { - super('flake8', Product.flake8, outputChannel, workspaceRootPath); + constructor(outputChannel: OutputChannel) { + super('flake8', Product.flake8, outputChannel); } public isEnabled(): Boolean { @@ -29,7 +29,7 @@ export class Linter extends baseLinter.BaseLinter { } return new Promise((resolve, reject) => { - this.run(flake8Path, flake8Args.concat(['--format=%(row)d,%(col)d,%(code).1s,%(code)s:%(text)s', document.uri.fsPath]), document, this.workspaceRootPath, cancellation).then(messages => { + this.run(flake8Path, flake8Args.concat(['--format=%(row)d,%(col)d,%(code).1s,%(code)s:%(text)s', document.uri.fsPath]), document, this.getWorkspaceRootPath(document), cancellation).then(messages => { messages.forEach(msg => { msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.flake8CategorySeverity); }); diff --git a/src/client/linters/main.ts b/src/client/linters/main.ts index a3d8691c3508..a54d75fc7df9 100644 --- a/src/client/linters/main.ts +++ b/src/client/linters/main.ts @@ -12,32 +12,32 @@ import * as pydocstyle from './../linters/pydocstyle'; import * as mypy from './../linters/mypy'; export class LinterFactor { - public static createLinter(product: Product, outputChannel: OutputChannel, workspaceRootPath: string = workspace.rootPath): BaseLinter { + public static createLinter(product: Product, outputChannel: OutputChannel): BaseLinter { switch (product) { case Product.flake8: { - return new flake8.Linter(outputChannel, workspaceRootPath); + return new flake8.Linter(outputChannel); } case Product.mypy: { - return new mypy.Linter(outputChannel, workspaceRootPath); + return new mypy.Linter(outputChannel); } case Product.pep8: { - return new pep8.Linter(outputChannel, workspaceRootPath); + return new pep8.Linter(outputChannel); } case Product.prospector: { - return new prospector.Linter(outputChannel, workspaceRootPath); + return new prospector.Linter(outputChannel); } case Product.pydocstyle: { - return new pydocstyle.Linter(outputChannel, workspaceRootPath); + return new pydocstyle.Linter(outputChannel); } case Product.pylama: { - return new pylama.Linter(outputChannel, workspaceRootPath); + return new pylama.Linter(outputChannel); } case Product.pylint: { - return new pylint.Linter(outputChannel, workspaceRootPath); + return new pylint.Linter(outputChannel); } default: { throw new Error(`Invalid Linter '${Product[product]}''`); } } } -} \ No newline at end of file +} diff --git a/src/client/linters/mypy.ts b/src/client/linters/mypy.ts index db9d9d4c5edb..407c218bc08f 100644 --- a/src/client/linters/mypy.ts +++ b/src/client/linters/mypy.ts @@ -8,8 +8,8 @@ import { TextDocument, CancellationToken } from 'vscode'; const REGEX = '(?.py):(?\\d+): (?\\w+): (?.*)\\r?(\\n|$)'; export class Linter extends baseLinter.BaseLinter { - constructor(outputChannel: OutputChannel, workspaceRootPath?: string) { - super('mypy', Product.mypy, outputChannel, workspaceRootPath); + constructor(outputChannel: OutputChannel) { + super('mypy', Product.mypy, outputChannel); } public isEnabled(): Boolean { @@ -29,7 +29,7 @@ export class Linter extends baseLinter.BaseLinter { } return new Promise((resolve, reject) => { - this.run(mypyPath, mypyArgs.concat([document.uri.fsPath]), document, this.workspaceRootPath, cancellation, REGEX).then(messages => { + this.run(mypyPath, mypyArgs.concat([document.uri.fsPath]), document, this.getWorkspaceRootPath(document), cancellation, REGEX).then(messages => { messages.forEach(msg => { msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.mypyCategorySeverity); msg.code = msg.type; @@ -39,4 +39,4 @@ export class Linter extends baseLinter.BaseLinter { }, reject); }); } -} \ No newline at end of file +} diff --git a/src/client/linters/pep8Linter.ts b/src/client/linters/pep8Linter.ts index 51a4170b1e34..bb42f3c30834 100644 --- a/src/client/linters/pep8Linter.ts +++ b/src/client/linters/pep8Linter.ts @@ -8,8 +8,8 @@ import { TextDocument, CancellationToken } from 'vscode'; export class Linter extends baseLinter.BaseLinter { _columnOffset = 1; - constructor(outputChannel: OutputChannel, workspaceRootPath?: string) { - super('pep8', Product.pep8, outputChannel, workspaceRootPath); + constructor(outputChannel: OutputChannel) { + super('pep8', Product.pep8, outputChannel); } public isEnabled(): Boolean { @@ -29,7 +29,7 @@ export class Linter extends baseLinter.BaseLinter { } return new Promise(resolve => { - this.run(pep8Path, pep8Args.concat(['--format=%(row)d,%(col)d,%(code).1s,%(code)s:%(text)s', document.uri.fsPath]), document, this.workspaceRootPath, cancellation).then(messages => { + this.run(pep8Path, pep8Args.concat(['--format=%(row)d,%(col)d,%(code).1s,%(code)s:%(text)s', document.uri.fsPath]), document, this.getWorkspaceRootPath(document), cancellation).then(messages => { messages.forEach(msg => { msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.pep8CategorySeverity); }); diff --git a/src/client/linters/prospector.ts b/src/client/linters/prospector.ts index 1d8a16544bbe..42c14dfd76e2 100644 --- a/src/client/linters/prospector.ts +++ b/src/client/linters/prospector.ts @@ -24,8 +24,8 @@ interface IProspectorLocation { } export class Linter extends baseLinter.BaseLinter { - constructor(outputChannel: OutputChannel, workspaceRootPath?: string) { - super('prospector', Product.prospector, outputChannel, workspaceRootPath); + constructor(outputChannel: OutputChannel) { + super('prospector', Product.prospector, outputChannel); } public isEnabled(): Boolean { @@ -46,7 +46,7 @@ export class Linter extends baseLinter.BaseLinter { } return new Promise((resolve, reject) => { - execPythonFile(prospectorPath, prospectorArgs.concat(['--absolute-paths', '--output-format=json', document.uri.fsPath]), this.workspaceRootPath, false, null, cancellation).then(data => { + execPythonFile(prospectorPath, prospectorArgs.concat(['--absolute-paths', '--output-format=json', document.uri.fsPath]), this.getWorkspaceRootPath(document), false, null, cancellation).then(data => { let parsedData: IProspectorResponse; try { parsedData = JSON.parse(data); diff --git a/src/client/linters/pydocstyle.ts b/src/client/linters/pydocstyle.ts index 98f32bbe210b..b361bf1640ec 100644 --- a/src/client/linters/pydocstyle.ts +++ b/src/client/linters/pydocstyle.ts @@ -9,8 +9,8 @@ import { Product, ProductExecutableAndArgs } from '../common/installer'; import { TextDocument, CancellationToken } from 'vscode'; export class Linter extends baseLinter.BaseLinter { - constructor(outputChannel: OutputChannel, workspaceRootPath?: string) { - super('pydocstyle', Product.pydocstyle, outputChannel, workspaceRootPath); + constructor(outputChannel: OutputChannel) { + super('pydocstyle', Product.pydocstyle, outputChannel); } public isEnabled(): Boolean { @@ -45,7 +45,7 @@ export class Linter extends baseLinter.BaseLinter { let outputChannel = this.outputChannel; return new Promise((resolve, reject) => { - execPythonFile(commandLine, args, this.workspaceRootPath, true, null, cancellation).then(data => { + execPythonFile(commandLine, args, this.getWorkspaceRootPath(document), true, null, cancellation).then(data => { outputChannel.append('#'.repeat(10) + 'Linting Output - ' + this.Id + '#'.repeat(10) + '\n'); outputChannel.append(data); let outputLines = data.split(/\r?\n/g); diff --git a/src/client/linters/pylama.ts b/src/client/linters/pylama.ts index 9faba1adeb33..718ec652adf4 100644 --- a/src/client/linters/pylama.ts +++ b/src/client/linters/pylama.ts @@ -10,8 +10,8 @@ const REGEX = '(?.py):(?\\d+):(?\\d+): \\[(?\\w+)\\] ( export class Linter extends baseLinter.BaseLinter { _columnOffset = 1; - constructor(outputChannel: OutputChannel, workspaceRootPath?: string) { - super('pylama', Product.pylama, outputChannel, workspaceRootPath); + constructor(outputChannel: OutputChannel) { + super('pylama', Product.pylama, outputChannel); } public isEnabled(): Boolean { @@ -31,7 +31,7 @@ export class Linter extends baseLinter.BaseLinter { } return new Promise(resolve => { - this.run(pylamaPath, pylamaArgs.concat(['--format=parsable', document.uri.fsPath]), document, this.workspaceRootPath, cancellation, REGEX).then(messages => { + this.run(pylamaPath, pylamaArgs.concat(['--format=parsable', document.uri.fsPath]), document, this.getWorkspaceRootPath(document), cancellation, REGEX).then(messages => { // All messages in pylama are treated as warnings for now messages.forEach(msg => { msg.severity = baseLinter.LintMessageSeverity.Information; diff --git a/src/client/linters/pylint.ts b/src/client/linters/pylint.ts index 5dfe6aaf0502..d485f7764ba1 100644 --- a/src/client/linters/pylint.ts +++ b/src/client/linters/pylint.ts @@ -6,8 +6,8 @@ import { Product, ProductExecutableAndArgs } from '../common/installer'; import { TextDocument, CancellationToken } from 'vscode'; export class Linter extends baseLinter.BaseLinter { - constructor(outputChannel: OutputChannel, workspaceRootPath?: string) { - super('pylint', Product.pylint, outputChannel, workspaceRootPath); + constructor(outputChannel: OutputChannel) { + super('pylint', Product.pylint, outputChannel); } public isEnabled(): Boolean { @@ -27,7 +27,7 @@ export class Linter extends baseLinter.BaseLinter { } return new Promise((resolve, reject) => { - this.run(pylintPath, pylintArgs.concat(['--msg-template=\'{line},{column},{category},{msg_id}:{msg}\'', '--reports=n', '--output-format=text', document.uri.fsPath]), document, this.workspaceRootPath, cancellation).then(messages => { + this.run(pylintPath, pylintArgs.concat(['--msg-template=\'{line},{column},{category},{msg_id}:{msg}\'', '--reports=n', '--output-format=text', document.uri.fsPath]), document, this.getWorkspaceRootPath(document), cancellation).then(messages => { messages.forEach(msg => { msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.pylintCategorySeverity); }); @@ -36,4 +36,4 @@ export class Linter extends baseLinter.BaseLinter { }, reject); }); } -} \ No newline at end of file +} diff --git a/src/client/providers/lintProvider.ts b/src/client/providers/lintProvider.ts index 830fc96e771f..165055bfccc8 100644 --- a/src/client/providers/lintProvider.ts +++ b/src/client/providers/lintProvider.ts @@ -135,7 +135,9 @@ export class LintProvider extends vscode.Disposable { private onLintDocument(document: vscode.TextDocument): void { // Check if we need to lint this document - const relativeFileName = typeof vscode.workspace.rootPath === 'string' ? path.relative(vscode.workspace.rootPath, document.fileName) : document.fileName; + const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); + const workspaceRootPath = (workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string') ? workspaceFolder.uri.fsPath : undefined; + const relativeFileName = typeof workspaceRootPath === 'string' ? path.relative(workspaceRootPath, document.fileName) : document.fileName; if (this.ignoreMinmatches.some(matcher => matcher.match(document.fileName) || matcher.match(relativeFileName))) { return; } @@ -154,7 +156,7 @@ export class LintProvider extends vscode.Disposable { this.pendingLintings.set(document.uri.fsPath, cancelToken); this.outputChannel.clear(); let promises: Promise[] = this.linters.map(linter => { - if (!vscode.workspace.rootPath && !this.settings.linting.enabledWithoutWorkspace) { + if (typeof workspaceRootPath !== 'string' && !this.settings.linting.enabledWithoutWorkspace) { return Promise.resolve([]); } if (!linter.isEnabled()) { diff --git a/src/test/linters/lint.test.ts b/src/test/linters/lint.test.ts index 3f52789584cf..45657a478476 100644 --- a/src/test/linters/lint.test.ts +++ b/src/test/linters/lint.test.ts @@ -152,23 +152,23 @@ suite('Linting', () => { } test('Enable and Disable Pylint', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new pyLint.Linter(ch, pythoFilesPath), 'pylintEnabled'); + testEnablingDisablingOfLinter(new pyLint.Linter(ch), 'pylintEnabled'); }); test('Enable and Disable Pep8', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new pep8.Linter(ch, pythoFilesPath), 'pep8Enabled'); + testEnablingDisablingOfLinter(new pep8.Linter(ch), 'pep8Enabled'); }); test('Enable and Disable Flake8', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new flake8.Linter(ch, pythoFilesPath), 'flake8Enabled'); + testEnablingDisablingOfLinter(new flake8.Linter(ch), 'flake8Enabled'); }); test('Enable and Disable Prospector', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new prospector.Linter(ch, pythoFilesPath), 'prospectorEnabled'); + testEnablingDisablingOfLinter(new prospector.Linter(ch), 'prospectorEnabled'); }); test('Enable and Disable Pydocstyle', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new pydocstyle.Linter(ch, pythoFilesPath), 'pydocstyleEnabled'); + testEnablingDisablingOfLinter(new pydocstyle.Linter(ch), 'pydocstyleEnabled'); }); function disableAllButThisLinter(linterToEnable: Product) { @@ -215,23 +215,23 @@ suite('Linting', () => { } test('PyLint', done => { let ch = new MockOutputChannel('Lint'); - let linter = new pyLint.Linter(ch, pythoFilesPath); + let linter = new pyLint.Linter(ch); testLinterMessages(linter, ch, fileToLint, pylintMessagesToBeReturned).then(done, done); }); test('Flake8', done => { let ch = new MockOutputChannel('Lint'); - let linter = new flake8.Linter(ch, pythoFilesPath); + let linter = new flake8.Linter(ch); testLinterMessages(linter, ch, fileToLint, flake8MessagesToBeReturned).then(done, done); }); test('Pep8', done => { let ch = new MockOutputChannel('Lint'); - let linter = new pep8.Linter(ch, pythoFilesPath); + let linter = new pep8.Linter(ch); testLinterMessages(linter, ch, fileToLint, pep8MessagesToBeReturned).then(done, done); }); if (!isPython3) { test('Pydocstyle', done => { let ch = new MockOutputChannel('Lint'); - let linter = new pydocstyle.Linter(ch, pythoFilesPath); + let linter = new pydocstyle.Linter(ch); testLinterMessages(linter, ch, fileToLint, pydocstyleMessagseToBeReturned).then(done, done); }); } @@ -242,26 +242,26 @@ suite('Linting', () => { const messagesToBeReturned = value ? filteredPylint3MessagesToBeReturned : filteredPylintMessagesToBeReturned; test('PyLint with config in root', done => { let ch = new MockOutputChannel('Lint'); - let linter = new pyLint.Linter(ch, pylintConfigPath); + let linter = new pyLint.Linter(ch); testLinterMessages(linter, ch, path.join(pylintConfigPath, 'file.py'), messagesToBeReturned).then(done, done); }); }); } test('Flake8 with config in root', done => { let ch = new MockOutputChannel('Lint'); - let linter = new flake8.Linter(ch, flake8ConfigPath); + let linter = new flake8.Linter(ch); testLinterMessages(linter, ch, path.join(flake8ConfigPath, 'file.py'), filteredFlake8MessagesToBeReturned).then(done, done); }); test('Pep8 with config in root', done => { let ch = new MockOutputChannel('Lint'); - let linter = new pep8.Linter(ch, pep8ConfigPath); + let linter = new pep8.Linter(ch); testLinterMessages(linter, ch, path.join(pep8ConfigPath, 'file.py'), filteredPep88MessagesToBeReturned).then(done, done); }); isPython3.then(value => { const messagesToBeReturned = value ? [] : fiteredPydocstyleMessagseToBeReturned; test('Pydocstyle with config in root', done => { let ch = new MockOutputChannel('Lint'); - let linter = new pydocstyle.Linter(ch, pydocstyleConfigPath27); + let linter = new pydocstyle.Linter(ch); testLinterMessages(linter, ch, path.join(pydocstyleConfigPath27, 'file.py'), messagesToBeReturned).then(done, done); }); }); From a549fc67229b149d99bab2aaf7b7bfce020e967a Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 6 Oct 2017 08:43:28 -0700 Subject: [PATCH 04/57] multi root support for formatters --- src/client/formatters/autoPep8Formatter.ts | 6 +++--- src/client/formatters/baseFormatter.ts | 11 ++++++++--- src/client/formatters/dummyFormatter.ts | 6 +++--- src/client/formatters/yapfFormatter.ts | 6 +++--- src/client/providers/formatOnSaveProvider.ts | 11 +++++++---- src/test/format/extension.format.test.ts | 17 ++++++++++------- src/test/initialize.ts | 2 +- 7 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/client/formatters/autoPep8Formatter.ts b/src/client/formatters/autoPep8Formatter.ts index 8a81c1dc01be..35b496719278 100644 --- a/src/client/formatters/autoPep8Formatter.ts +++ b/src/client/formatters/autoPep8Formatter.ts @@ -6,8 +6,8 @@ import * as settings from '../common/configSettings'; import { Product } from '../common/installer'; export class AutoPep8Formatter extends BaseFormatter { - constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings, workspaceRootPath?: string) { - super('autopep8', Product.autopep8, outputChannel, pythonSettings, workspaceRootPath); + constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings) { + super('autopep8', Product.autopep8, outputChannel, pythonSettings); } public formatDocument(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, range?: vscode.Range): Thenable { @@ -19,4 +19,4 @@ export class AutoPep8Formatter extends BaseFormatter { } return super.provideDocumentFormattingEdits(document, options, token, autopep8Path, autoPep8Args); } -} \ No newline at end of file +} diff --git a/src/client/formatters/baseFormatter.ts b/src/client/formatters/baseFormatter.ts index 032f9bf61619..b60dc9b7dfd1 100644 --- a/src/client/formatters/baseFormatter.ts +++ b/src/client/formatters/baseFormatter.ts @@ -2,15 +2,16 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; -import { execPythonFile } from './../common/utils'; +import * as path from 'path'; import * as settings from './../common/configSettings'; +import { execPythonFile } from './../common/utils'; import { getTextEditsFromPatch, getTempFileWithDocumentContents } from './../common/editor'; import { isNotInstalledError } from '../common/helpers'; import { Installer, Product } from '../common/installer'; export abstract class BaseFormatter { private installer: Installer; - constructor(public Id: string, private product: Product, protected outputChannel: vscode.OutputChannel, protected pythonSettings: settings.IPythonSettings, protected workspaceRootPath?: string) { + constructor(public Id: string, private product: Product, protected outputChannel: vscode.OutputChannel, protected pythonSettings: settings.IPythonSettings) { this.installer = new Installer(); } @@ -18,7 +19,11 @@ export abstract class BaseFormatter { protected provideDocumentFormattingEdits(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, command: string, args: string[], cwd: string = null): Thenable { this.outputChannel.clear(); - cwd = typeof cwd === 'string' && cwd.length > 0 ? cwd : (this.workspaceRootPath ? this.workspaceRootPath : vscode.workspace.rootPath); + if (typeof cwd !== 'string' || cwd.length === 0) { + const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); + const workspaceRootPath = (workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string') ? workspaceFolder.uri.fsPath : path.dirname(document.uri.fsPath); + cwd = workspaceRootPath; + } // autopep8 and yapf have the ability to read from the process input stream and return the formatted code out of the output stream // However they don't support returning the diff of the formatted text when reading data from the input stream diff --git a/src/client/formatters/dummyFormatter.ts b/src/client/formatters/dummyFormatter.ts index a008987fde92..252fdd685209 100644 --- a/src/client/formatters/dummyFormatter.ts +++ b/src/client/formatters/dummyFormatter.ts @@ -6,11 +6,11 @@ import * as settings from './../common/configSettings'; import { Product } from '../common/installer'; export class DummyFormatter extends BaseFormatter { - constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings, workspaceRootPath?: string) { - super('none', Product.yapf, outputChannel, pythonSettings, workspaceRootPath); + constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings) { + super('none', Product.yapf, outputChannel, pythonSettings); } public formatDocument(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, range?: vscode.Range): Thenable { return Promise.resolve([]); } -} \ No newline at end of file +} diff --git a/src/client/formatters/yapfFormatter.ts b/src/client/formatters/yapfFormatter.ts index 7a858bd9f869..f9474ea841f9 100644 --- a/src/client/formatters/yapfFormatter.ts +++ b/src/client/formatters/yapfFormatter.ts @@ -7,8 +7,8 @@ import { Product } from '../common/installer'; import * as path from 'path'; export class YapfFormatter extends BaseFormatter { - constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings, workspaceRootPath?: string) { - super('yapf', Product.yapf, outputChannel, pythonSettings, workspaceRootPath); + constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings) { + super('yapf', Product.yapf, outputChannel, pythonSettings); } public formatDocument(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, range?: vscode.Range): Thenable { @@ -22,4 +22,4 @@ export class YapfFormatter extends BaseFormatter { let cwd = path.dirname(document.fileName); return super.provideDocumentFormattingEdits(document, options, token, yapfPath, yapfArgs, cwd); } -} \ No newline at end of file +} diff --git a/src/client/providers/formatOnSaveProvider.ts b/src/client/providers/formatOnSaveProvider.ts index 4e972ff38c9b..b3a2ec3753bf 100644 --- a/src/client/providers/formatOnSaveProvider.ts +++ b/src/client/providers/formatOnSaveProvider.ts @@ -6,17 +6,20 @@ import * as vscode from "vscode"; import { BaseFormatter } from "./../formatters/baseFormatter"; import { YapfFormatter } from "./../formatters/yapfFormatter"; import { AutoPep8Formatter } from "./../formatters/autoPep8Formatter"; +import { DummyFormatter } from "./../formatters/dummyFormatter"; import * as settings from "./../common/configSettings"; -export function activateFormatOnSaveProvider(languageFilter: vscode.DocumentFilter, settings: settings.IPythonSettings, outputChannel: vscode.OutputChannel, workspaceRootPath?: string): vscode.Disposable { +export function activateFormatOnSaveProvider(languageFilter: vscode.DocumentFilter, settings: settings.IPythonSettings, outputChannel: vscode.OutputChannel): vscode.Disposable { let formatters = new Map(); let pythonSettings = settings; - let yapfFormatter = new YapfFormatter(outputChannel, settings, workspaceRootPath); - let autoPep8 = new AutoPep8Formatter(outputChannel, settings, workspaceRootPath); + let yapfFormatter = new YapfFormatter(outputChannel, settings); + let autoPep8 = new AutoPep8Formatter(outputChannel, settings); + const dummyFormatter = new DummyFormatter(outputChannel, settings); formatters.set(yapfFormatter.Id, yapfFormatter); formatters.set(autoPep8.Id, autoPep8); + formatters.set(dummyFormatter.Id, dummyFormatter); return vscode.workspace.onWillSaveTextDocument(e => { const document = e.document; @@ -31,4 +34,4 @@ export function activateFormatOnSaveProvider(languageFilter: vscode.DocumentFilt e.waitUntil(formatter.formatDocument(document, null, null)); } }, null, null); -} \ No newline at end of file +} diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index 5cfa45dabd86..e764976bcf80 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -12,6 +12,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import * as fs from 'fs-extra'; +import { EOL } from 'os'; import { AutoPep8Formatter } from '../../client/formatters/autoPep8Formatter'; import { initialize, IS_TRAVIS, closeActiveWindows } from '../initialize'; import { YapfFormatter } from '../../client/formatters/yapfFormatter'; @@ -38,8 +39,9 @@ suite('Formatting', () => { fs.copySync(originalUnformattedFile, file, { overwrite: true }); }); fs.ensureDirSync(path.dirname(autoPep8FileToFormat)); - const yapf = execPythonFile('yapf', [originalUnformattedFile], pythoFilesPath, false); - const autoPep8 = execPythonFile('autopep8', [originalUnformattedFile], pythoFilesPath, false); + const workspaceRoot = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(originalUnformattedFile)).uri.fsPath; + const yapf = execPythonFile('yapf', [originalUnformattedFile], workspaceRoot, false); + const autoPep8 = execPythonFile('autopep8', [originalUnformattedFile], workspaceRoot, false); await Promise.all([yapf, autoPep8]).then(formattedResults => { formattedYapf = formattedResults[0]; formattedAutoPep8 = formattedResults[1]; @@ -76,11 +78,11 @@ suite('Formatting', () => { }); } test('AutoPep8', done => { - testFormatting(new AutoPep8Formatter(ch, pythonSettings, pythoFilesPath), formattedAutoPep8, autoPep8FileToFormat).then(done, done); + testFormatting(new AutoPep8Formatter(ch, pythonSettings), formattedAutoPep8, autoPep8FileToFormat).then(done, done); }); test('Yapf', done => { - testFormatting(new YapfFormatter(ch, pythonSettings, pythoFilesPath), formattedYapf, yapfFileToFormat).then(done, done); + testFormatting(new YapfFormatter(ch, pythonSettings), formattedYapf, yapfFileToFormat).then(done, done); }); function testAutoFormatting(formatter: string, formattedContents: string, fileToFormat: string): PromiseLike { @@ -104,17 +106,18 @@ suite('Formatting', () => { }, 5000); }); }).then(() => { - assert.equal(textDocument.getText(), formattedContents, 'Formatted contents are not the same'); + const text = textDocument.getText(); + assert.equal(text === formattedContents, true, 'Formatted contents are not the same'); }); } test('AutoPep8 autoformat on save', done => { - testAutoFormatting('autopep8', '#\n' + formattedAutoPep8, autoPep8FileToAutoFormat).then(done, done); + testAutoFormatting('autopep8', `#${EOL}` + formattedAutoPep8, autoPep8FileToAutoFormat).then(done, done); }); // For some reason doesn't ever work on travis if (!IS_TRAVIS) { test('Yapf autoformat on save', done => { - testAutoFormatting('yapf', '#\n' + formattedYapf, yapfFileToAutoFormat).then(done, done); + testAutoFormatting('yapf', `#${EOL}` + formattedYapf, yapfFileToAutoFormat).then(done, done); }); } }); diff --git a/src/test/initialize.ts b/src/test/initialize.ts index a298c234c4a9..1dcd6e247fd4 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -71,7 +71,7 @@ function getPythonPath(): string { const pythonPaths = ['/home/travis/virtualenv/python3.5.2/bin/python', '/xUsers/travis/.pyenv/versions/3.5.1/envs/MYVERSION/bin/python', '/xUsers/donjayamanne/Projects/PythonEnvs/p361/bin/python', - 'C:/Users/dojayama/nine/python.exe', + 'cC:/Users/dojayama/nine/python.exe', 'C:/Development/PythonEnvs/p27/scripts/python.exe', '/Users/donjayamanne/Projects/PythonEnvs/p27/bin/python']; for (let counter = 0; counter < pythonPaths.length; counter++) { From 657e20f4a12b0ec00b77796c5381a5402ee41d3b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 6 Oct 2017 08:57:38 -0700 Subject: [PATCH 05/57] determine workspace root path --- src/test/format/extension.format.test.ts | 6 +++--- src/test/index.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index e764976bcf80..530cbd0b274f 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -22,6 +22,7 @@ const pythonSettings = settings.PythonSettings.getInstance(); const ch = vscode.window.createOutputChannel('Tests'); const pythoFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'formatting'); +const workspaceRootPath = path.join(__dirname, '..', '..', '..', 'src', 'test'); const originalUnformattedFile = path.join(pythoFilesPath, 'fileToFormat.py'); const autoPep8FileToFormat = path.join(pythoFilesPath, 'autoPep8FileToFormat.py'); @@ -39,9 +40,8 @@ suite('Formatting', () => { fs.copySync(originalUnformattedFile, file, { overwrite: true }); }); fs.ensureDirSync(path.dirname(autoPep8FileToFormat)); - const workspaceRoot = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(originalUnformattedFile)).uri.fsPath; - const yapf = execPythonFile('yapf', [originalUnformattedFile], workspaceRoot, false); - const autoPep8 = execPythonFile('autopep8', [originalUnformattedFile], workspaceRoot, false); + const yapf = execPythonFile('yapf', [originalUnformattedFile], workspaceRootPath, false); + const autoPep8 = execPythonFile('autopep8', [originalUnformattedFile], workspaceRootPath, false); await Promise.all([yapf, autoPep8]).then(formattedResults => { formattedYapf = formattedResults[0]; formattedAutoPep8 = formattedResults[1]; diff --git a/src/test/index.ts b/src/test/index.ts index af916a981db9..064f575abb67 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -18,7 +18,8 @@ let testRunner = require('vscode/lib/testrunner'); testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results - timeout: 25000 + timeout: 25000, + grep:'Format' }); initializePython(); From d492b5ab5bacf4bfd17fe42af8a3a09594cd13c8 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 6 Oct 2017 15:04:03 -0700 Subject: [PATCH 06/57] revert change --- src/test/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index 064f575abb67..af916a981db9 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -18,8 +18,7 @@ let testRunner = require('vscode/lib/testrunner'); testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results - timeout: 25000, - grep:'Format' + timeout: 25000 }); initializePython(); From c0c70fbdeb263b5e50dc4674ca297713f262f83c Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 6 Oct 2017 15:20:43 -0700 Subject: [PATCH 07/57] support multiple configs per workspace folder --- src/client/common/configSettings.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index 2897b50eee1b..de0bbf614dd1 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -3,6 +3,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as child_process from 'child_process'; +import { Uri } from 'vscode'; import { SystemVariables } from './systemVariables'; import { EventEmitter } from 'events'; const untildify = require('untildify'); @@ -133,26 +134,32 @@ export interface JupyterSettings { const IS_TEST_EXECUTION = process.env['PYTHON_DONJAYAMANNE_TEST'] === '1'; export class PythonSettings extends EventEmitter implements IPythonSettings { - private static pythonSettings: PythonSettings = new PythonSettings(); + private static pythonSettings: Map = new Map(); + private workspaceRoot?: vscode.Uri; private disposables: vscode.Disposable[] = []; - constructor() { + constructor(workspaceFolder?: Uri) { super(); - if (PythonSettings.pythonSettings) { - throw new Error('Singleton class, Use getInstance method'); - } + this.workspaceRoot = workspaceFolder ? workspaceFolder : vscode.Uri.file(__dirname); this.disposables.push(vscode.workspace.onDidChangeConfiguration(() => { this.initializeSettings(); })); this.initializeSettings(); } - public static getInstance(): PythonSettings { - return PythonSettings.pythonSettings; + public static getInstance(resource?: Uri): PythonSettings { + const workspaceFolder = resource ? vscode.workspace.getWorkspaceFolder(resource) : undefined; + const workspaceFolderUri = workspaceFolder ? workspaceFolder.uri : undefined; + const workspaceFolderKey = workspaceFolderUri ? workspaceFolderUri.fsPath : ''; + if (!PythonSettings.pythonSettings.has(workspaceFolderKey)) { + const settings = new PythonSettings(workspaceFolderUri); + PythonSettings.pythonSettings.set(workspaceFolderKey, settings); + } + return PythonSettings.pythonSettings.get(workspaceFolderKey); } private initializeSettings() { const systemVariables: SystemVariables = new SystemVariables(); - const workspaceRoot = (IS_TEST_EXECUTION || typeof vscode.workspace.rootPath !== 'string') ? __dirname : vscode.workspace.rootPath; - let pythonSettings = vscode.workspace.getConfiguration('python'); + const workspaceRoot = (IS_TEST_EXECUTION || !this.workspaceRoot) ? __dirname : this.workspaceRoot.fsPath; + const pythonSettings = vscode.workspace.getConfiguration('python', this.workspaceRoot); this.pythonPath = systemVariables.resolveAny(pythonSettings.get('pythonPath'))!; this.pythonPath = getAbsolutePath(this.pythonPath, IS_TEST_EXECUTION ? __dirname : workspaceRoot); this.venvPath = systemVariables.resolveAny(pythonSettings.get('venvPath'))!; From 86e36c1337bbe952493323b54245c23402c9a7d8 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 6 Oct 2017 15:52:11 -0700 Subject: [PATCH 08/57] modify formatters to use resource specific settings --- src/client/common/configSettings.ts | 5 +++- src/client/extension.ts | 4 ++-- src/client/formatters/autoPep8Formatter.ts | 11 +++++---- src/client/formatters/baseFormatter.ts | 25 ++++++++++++++++---- src/client/formatters/dummyFormatter.ts | 5 ++-- src/client/formatters/yapfFormatter.ts | 16 +++++++------ src/client/providers/formatOnSaveProvider.ts | 23 +++++++++--------- src/client/providers/formatProvider.ts | 13 +++++----- src/test/format/extension.format.test.ts | 4 ++-- 9 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index de0bbf614dd1..32ef9ec060fb 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -148,7 +148,10 @@ export class PythonSettings extends EventEmitter implements IPythonSettings { } public static getInstance(resource?: Uri): PythonSettings { const workspaceFolder = resource ? vscode.workspace.getWorkspaceFolder(resource) : undefined; - const workspaceFolderUri = workspaceFolder ? workspaceFolder.uri : undefined; + let workspaceFolderUri: Uri = workspaceFolder ? workspaceFolder.uri : undefined; + if (!workspaceFolderUri && Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0) { + workspaceFolderUri = vscode.workspace.workspaceFolders[0].uri; + } const workspaceFolderKey = workspaceFolderUri ? workspaceFolderUri.fsPath : ''; if (!PythonSettings.pythonSettings.has(workspaceFolderKey)) { const settings = new PythonSettings(workspaceFolderUri); diff --git a/src/client/extension.ts b/src/client/extension.ts index 4a468327b959..90f5c7d2e2ce 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -67,7 +67,7 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(...activateExecInTerminalProvider()); context.subscriptions.push(activateUpdateSparkLibraryProvider()); activateSimplePythonRefactorProvider(context, formatOutChannel); - context.subscriptions.push(activateFormatOnSaveProvider(PYTHON, settings.PythonSettings.getInstance(), formatOutChannel)); + context.subscriptions.push(activateFormatOnSaveProvider(PYTHON, formatOutChannel)); context.subscriptions.push(activateGoToObjectDefinitionProvider(context)); context.subscriptions.push(vscode.commands.registerCommand(Commands.Start_REPL, () => { @@ -114,7 +114,7 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(context, jediProx), '(', ',')); } if (pythonSettings.formatting.provider !== 'none') { - const formatProvider = new PythonFormattingEditProvider(context, formatOutChannel, pythonSettings); + const formatProvider = new PythonFormattingEditProvider(context, formatOutChannel); context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(PYTHON, formatProvider)); context.subscriptions.push(vscode.languages.registerDocumentRangeFormattingEditProvider(PYTHON, formatProvider)); } diff --git a/src/client/formatters/autoPep8Formatter.ts b/src/client/formatters/autoPep8Formatter.ts index 35b496719278..35afef4a57cf 100644 --- a/src/client/formatters/autoPep8Formatter.ts +++ b/src/client/formatters/autoPep8Formatter.ts @@ -2,17 +2,18 @@ import * as vscode from 'vscode'; import { BaseFormatter } from './baseFormatter'; -import * as settings from '../common/configSettings'; +import { PythonSettings } from '../common/configSettings'; import { Product } from '../common/installer'; export class AutoPep8Formatter extends BaseFormatter { - constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings) { - super('autopep8', Product.autopep8, outputChannel, pythonSettings); + constructor(outputChannel: vscode.OutputChannel) { + super('autopep8', Product.autopep8, outputChannel); } public formatDocument(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, range?: vscode.Range): Thenable { - let autopep8Path = this.pythonSettings.formatting.autopep8Path; - let autoPep8Args = Array.isArray(this.pythonSettings.formatting.autopep8Args) ? this.pythonSettings.formatting.autopep8Args : []; + const settings = PythonSettings.getInstance(document.uri); + const autopep8Path = settings.formatting.autopep8Path; + let autoPep8Args = Array.isArray(settings.formatting.autopep8Args) ? settings.formatting.autopep8Args : []; autoPep8Args = autoPep8Args.concat(['--diff']); if (range && !range.isEmpty) { autoPep8Args = autoPep8Args.concat(['--line-range', (range.start.line + 1).toString(), (range.end.line + 1).toString()]); diff --git a/src/client/formatters/baseFormatter.ts b/src/client/formatters/baseFormatter.ts index b60dc9b7dfd1..3fcce0d76661 100644 --- a/src/client/formatters/baseFormatter.ts +++ b/src/client/formatters/baseFormatter.ts @@ -4,25 +4,40 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; import * as settings from './../common/configSettings'; +import { Uri } from 'vscode'; import { execPythonFile } from './../common/utils'; import { getTextEditsFromPatch, getTempFileWithDocumentContents } from './../common/editor'; import { isNotInstalledError } from '../common/helpers'; import { Installer, Product } from '../common/installer'; + export abstract class BaseFormatter { private installer: Installer; - constructor(public Id: string, private product: Product, protected outputChannel: vscode.OutputChannel, protected pythonSettings: settings.IPythonSettings) { + constructor(public Id: string, private product: Product, protected outputChannel: vscode.OutputChannel) { this.installer = new Installer(); } public abstract formatDocument(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, range?: vscode.Range): Thenable; - + protected getDocumentPath(document: vscode.TextDocument, fallbackPath: string) { + if (path.basename(document.uri.fsPath) === document.uri.fsPath) { + return fallbackPath; + } + return path.dirname(document.fileName); + } + protected getWorkspaceUri(document: vscode.TextDocument) { + const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); + if (workspaceFolder) { + return workspaceFolder.uri; + } + if (Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0) { + return vscode.workspace.workspaceFolders[0].uri; + } + return vscode.Uri.file(__dirname); + } protected provideDocumentFormattingEdits(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, command: string, args: string[], cwd: string = null): Thenable { this.outputChannel.clear(); if (typeof cwd !== 'string' || cwd.length === 0) { - const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); - const workspaceRootPath = (workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string') ? workspaceFolder.uri.fsPath : path.dirname(document.uri.fsPath); - cwd = workspaceRootPath; + cwd = this.getWorkspaceUri(document).fsPath; } // autopep8 and yapf have the ability to read from the process input stream and return the formatted code out of the output stream diff --git a/src/client/formatters/dummyFormatter.ts b/src/client/formatters/dummyFormatter.ts index 252fdd685209..481b57c69ae6 100644 --- a/src/client/formatters/dummyFormatter.ts +++ b/src/client/formatters/dummyFormatter.ts @@ -2,12 +2,11 @@ import * as vscode from 'vscode'; import { BaseFormatter } from './baseFormatter'; -import * as settings from './../common/configSettings'; import { Product } from '../common/installer'; export class DummyFormatter extends BaseFormatter { - constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings) { - super('none', Product.yapf, outputChannel, pythonSettings); + constructor(outputChannel: vscode.OutputChannel) { + super('none', Product.yapf, outputChannel); } public formatDocument(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, range?: vscode.Range): Thenable { diff --git a/src/client/formatters/yapfFormatter.ts b/src/client/formatters/yapfFormatter.ts index f9474ea841f9..ff8e40e21066 100644 --- a/src/client/formatters/yapfFormatter.ts +++ b/src/client/formatters/yapfFormatter.ts @@ -1,25 +1,27 @@ 'use strict'; import * as vscode from 'vscode'; +import * as path from 'path'; import { BaseFormatter } from './baseFormatter'; -import * as settings from './../common/configSettings'; +import { PythonSettings } from '../common/configSettings'; import { Product } from '../common/installer'; -import * as path from 'path'; export class YapfFormatter extends BaseFormatter { - constructor(outputChannel: vscode.OutputChannel, pythonSettings: settings.IPythonSettings) { - super('yapf', Product.yapf, outputChannel, pythonSettings); + constructor(outputChannel: vscode.OutputChannel) { + super('yapf', Product.yapf, outputChannel); } public formatDocument(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken, range?: vscode.Range): Thenable { - let yapfPath = this.pythonSettings.formatting.yapfPath; - let yapfArgs = Array.isArray(this.pythonSettings.formatting.yapfArgs) ? this.pythonSettings.formatting.yapfArgs : []; + const settings = PythonSettings.getInstance(document.uri); + const yapfPath = settings.formatting.yapfPath; + let yapfArgs = Array.isArray(settings.formatting.yapfArgs) ? settings.formatting.yapfArgs : []; yapfArgs = yapfArgs.concat(['--diff']); if (range && !range.isEmpty) { yapfArgs = yapfArgs.concat(['--lines', `${range.start.line + 1}-${range.end.line + 1}`]); } // Yapf starts looking for config file starting from the file path - let cwd = path.dirname(document.fileName); + const fallbarFolder = this.getWorkspaceUri(document).fsPath; + const cwd = this.getDocumentPath(document, fallbarFolder); return super.provideDocumentFormattingEdits(document, options, token, yapfPath, yapfArgs, cwd); } } diff --git a/src/client/providers/formatOnSaveProvider.ts b/src/client/providers/formatOnSaveProvider.ts index b3a2ec3753bf..1b550f0d6cc4 100644 --- a/src/client/providers/formatOnSaveProvider.ts +++ b/src/client/providers/formatOnSaveProvider.ts @@ -7,15 +7,13 @@ import { BaseFormatter } from "./../formatters/baseFormatter"; import { YapfFormatter } from "./../formatters/yapfFormatter"; import { AutoPep8Formatter } from "./../formatters/autoPep8Formatter"; import { DummyFormatter } from "./../formatters/dummyFormatter"; -import * as settings from "./../common/configSettings"; +import { PythonSettings } from "./../common/configSettings"; -export function activateFormatOnSaveProvider(languageFilter: vscode.DocumentFilter, settings: settings.IPythonSettings, outputChannel: vscode.OutputChannel): vscode.Disposable { - let formatters = new Map(); - let pythonSettings = settings; - - let yapfFormatter = new YapfFormatter(outputChannel, settings); - let autoPep8 = new AutoPep8Formatter(outputChannel, settings); - const dummyFormatter = new DummyFormatter(outputChannel, settings); +export function activateFormatOnSaveProvider(languageFilter: vscode.DocumentFilter, outputChannel: vscode.OutputChannel): vscode.Disposable { + const formatters = new Map(); + const yapfFormatter = new YapfFormatter(outputChannel); + const autoPep8 = new AutoPep8Formatter(outputChannel); + const dummyFormatter = new DummyFormatter(outputChannel); formatters.set(yapfFormatter.Id, yapfFormatter); formatters.set(autoPep8.Id, autoPep8); @@ -26,11 +24,12 @@ export function activateFormatOnSaveProvider(languageFilter: vscode.DocumentFilt if (document.languageId !== languageFilter.language) { return; } - let textEditor = vscode.window.activeTextEditor; - let editorConfig = vscode.workspace.getConfiguration('editor'); + const textEditor = vscode.window.activeTextEditor; + const editorConfig = vscode.workspace.getConfiguration('editor'); const globalEditorFormatOnSave = editorConfig && editorConfig.has('formatOnSave') && editorConfig.get('formatOnSave') === true; - if ((pythonSettings.formatting.formatOnSave || globalEditorFormatOnSave) && textEditor.document === document) { - let formatter = formatters.get(pythonSettings.formatting.provider); + const settings = PythonSettings.getInstance(document.uri); + if ((settings.formatting.formatOnSave || globalEditorFormatOnSave) && textEditor.document === document) { + const formatter = formatters.get(settings.formatting.provider); e.waitUntil(formatter.formatDocument(document, null, null)); } }, null, null); diff --git a/src/client/providers/formatProvider.ts b/src/client/providers/formatProvider.ts index 5df3ab9e2cca..58844bca269c 100644 --- a/src/client/providers/formatProvider.ts +++ b/src/client/providers/formatProvider.ts @@ -5,15 +5,15 @@ import { BaseFormatter } from './../formatters/baseFormatter'; import { YapfFormatter } from './../formatters/yapfFormatter'; import { AutoPep8Formatter } from './../formatters/autoPep8Formatter'; import { DummyFormatter } from './../formatters/dummyFormatter'; -import * as settings from './../common/configSettings'; +import { PythonSettings } from './../common/configSettings'; export class PythonFormattingEditProvider implements vscode.DocumentFormattingEditProvider, vscode.DocumentRangeFormattingEditProvider { private formatters = new Map(); - public constructor(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel, private settings: settings.IPythonSettings) { - let yapfFormatter = new YapfFormatter(outputChannel, settings); - let autoPep8 = new AutoPep8Formatter(outputChannel, settings); - let dummy = new DummyFormatter(outputChannel, settings); + public constructor(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel) { + const yapfFormatter = new YapfFormatter(outputChannel); + const autoPep8 = new AutoPep8Formatter(outputChannel); + const dummy = new DummyFormatter(outputChannel); this.formatters.set(yapfFormatter.Id, yapfFormatter); this.formatters.set(autoPep8.Id, autoPep8); this.formatters.set(dummy.Id, dummy); @@ -24,7 +24,8 @@ export class PythonFormattingEditProvider implements vscode.DocumentFormattingEd } public provideDocumentRangeFormattingEdits(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable { - let formatter = this.formatters.get(this.settings.formatting.provider); + const settings = PythonSettings.getInstance(document.uri); + const formatter = this.formatters.get(settings.formatting.provider); return formatter.formatDocument(document, options, token, range); } diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index 530cbd0b274f..986d64e976a9 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -78,11 +78,11 @@ suite('Formatting', () => { }); } test('AutoPep8', done => { - testFormatting(new AutoPep8Formatter(ch, pythonSettings), formattedAutoPep8, autoPep8FileToFormat).then(done, done); + testFormatting(new AutoPep8Formatter(ch), formattedAutoPep8, autoPep8FileToFormat).then(done, done); }); test('Yapf', done => { - testFormatting(new YapfFormatter(ch, pythonSettings), formattedYapf, yapfFileToFormat).then(done, done); + testFormatting(new YapfFormatter(ch), formattedYapf, yapfFileToFormat).then(done, done); }); function testAutoFormatting(formatter: string, formattedContents: string, fileToFormat: string): PromiseLike { From c5cd8a9fc53013a0c79d7cccbefcae8ebba4747f Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 9 Oct 2017 13:18:53 -0700 Subject: [PATCH 09/57] modified installer to pass resource for workspace resolution --- src/client/common/installer.ts | 168 +++++++++++------- src/client/formatters/baseFormatter.ts | 6 +- src/client/linters/baseLinter.ts | 8 +- .../linters/errorHandlers/invalidArgs.ts | 6 +- src/client/linters/errorHandlers/main.ts | 4 +- .../linters/errorHandlers/notInstalled.ts | 5 +- src/client/linters/errorHandlers/standard.ts | 15 +- src/client/linters/prospector.ts | 6 +- src/client/linters/pydocstyle.ts | 2 +- src/client/providers/renameProvider.ts | 2 +- .../providers/simpleRefactorProvider.ts | 2 +- .../unittests/common/baseTestManager.ts | 2 +- src/client/workspaceSymbols/main.ts | 33 ++-- src/test/autocomplete/base.test.ts | 2 +- 14 files changed, 144 insertions(+), 117 deletions(-) diff --git a/src/client/common/installer.ts b/src/client/common/installer.ts index 1b3991047944..6d7af57228a5 100644 --- a/src/client/common/installer.ts +++ b/src/client/common/installer.ts @@ -1,9 +1,9 @@ import * as vscode from 'vscode'; import * as settings from './configSettings'; import * as os from 'os'; +import { commands, ConfigurationTarget, Disposable, OutputChannel, Terminal, Uri, window, workspace } from 'vscode'; import { isNotInstalledError } from './helpers'; import { execPythonFile, getFullyQualifiedPythonInterpreterPath } from './utils'; -import { Documentation } from './constants'; export enum Product { pytest, @@ -111,6 +111,9 @@ SettingToDisableProduct.set(Product.pydocstyle, 'linting.pydocstyleEnabled'); SettingToDisableProduct.set(Product.pylint, 'linting.pylintEnabled'); SettingToDisableProduct.set(Product.pytest, 'unitTest.pyTestEnabled'); +const ProductInstallationPrompt = new Map(); +ProductInstallationPrompt.set(Product.ctags, 'Install CTags to enable Python workspace symbols'); + enum ProductType { Linter, Formatter, @@ -142,6 +145,11 @@ ProductTypes.set(Product.autopep8, ProductType.Formatter); ProductTypes.set(Product.yapf, ProductType.Formatter); ProductTypes.set(Product.rope, ProductType.RefactoringLibrary); +export enum InstallerResponse { + Installed, + Disabled, + Ignore +} export class Installer implements vscode.Disposable { private static terminal: vscode.Terminal | undefined | null; private disposables: vscode.Disposable[] = []; @@ -155,12 +163,14 @@ export class Installer implements vscode.Disposable { public dispose() { this.disposables.forEach(d => d.dispose()); } - public shouldDisplayPrompt(product: Product) { + private shouldDisplayPrompt(product: Product) { const productName = ProductNames.get(product)!; - return settings.PythonSettings.getInstance().disablePromptForFeatures.indexOf(productName) === -1; + const pythonConfig = workspace.getConfiguration('python'); + const disablePromptForFeatures = pythonConfig.get('disablePromptForFeatures', [] as string[]); + return disablePromptForFeatures.indexOf(productName) === -1; } - async promptToInstall(product: Product) { + public async promptToInstall(product: Product, resource?: Uri): Promise { const productType = ProductTypes.get(product)!; const productTypeName = ProductTypeNames.get(productType); const productName = ProductNames.get(product)!; @@ -173,10 +183,10 @@ export class Installer implements vscode.Disposable { else { console.warn(message); } - return; + return InstallerResponse.Ignore; } - const installOption = 'Install ' + productName; + const installOption = ProductInstallationPrompt.has(product) ? ProductInstallationPrompt.get(product) : 'Install ' + productName; const disableOption = 'Disable ' + productTypeName; const dontShowAgain = `Don't show this prompt again`; const alternateFormatter = product === Product.autopep8 ? 'yapf' : 'autopep8'; @@ -189,46 +199,50 @@ export class Installer implements vscode.Disposable { if (SettingToDisableProduct.has(product)) { options.push(...[disableOption, dontShowAgain]); } - return vscode.window.showErrorMessage(`${productTypeName} ${productName} is not installed`, ...options).then(item => { - switch (item) { - case installOption: { - return this.install(product); - } - case disableOption: { - if (Linters.indexOf(product) >= 0) { - return disableLinter(product); - } - else { - const pythonConfig = vscode.workspace.getConfiguration('python'); - const settingToDisable = SettingToDisableProduct.get(product)!; - return pythonConfig.update(settingToDisable, false); - } - } - case useOtherFormatter: { - const pythonConfig = vscode.workspace.getConfiguration('python'); - return pythonConfig.update('formatting.provider', alternateFormatter); - } - case dontShowAgain: { - const pythonConfig = vscode.workspace.getConfiguration('python'); - const features = pythonConfig.get('disablePromptForFeatures', [] as string[]); - features.push(productName); - return pythonConfig.update('disablePromptForFeatures', features, true); + const item = await window.showErrorMessage(`${productTypeName} ${productName} is not installed`, ...options); + switch (item) { + case installOption: { + return this.install(product, resource); + } + case disableOption: { + if (Linters.indexOf(product) >= 0) { + return this.disableLinter(product, resource).then(() => InstallerResponse.Disabled); } - case 'Help': { - return Promise.resolve(); + else { + const settingToDisable = SettingToDisableProduct.get(product)!; + return this.updateSetting(settingToDisable, false, resource).then(() => InstallerResponse.Disabled); } } - }); + case useOtherFormatter: { + return this.updateSetting('formatting.provider', alternateFormatter, resource) + .then(() => InstallerResponse.Installed); + } + case dontShowAgain: { + const pythonConfig = workspace.getConfiguration('python'); + const features = pythonConfig.get('disablePromptForFeatures', [] as string[]); + features.push(productName); + return pythonConfig.update('disablePromptForFeatures', features, true).then(() => InstallerResponse.Ignore); + } + } } - - install(product: Product): Promise { + public async install(product: Product, resource?: Uri): Promise { if (!this.outputChannel && !Installer.terminal) { - Installer.terminal = vscode.window.createTerminal('Python Installer'); + Installer.terminal = window.createTerminal('Python Installer'); } - if (product === Product.ctags && os.platform() === 'win32') { - vscode.commands.executeCommand('python.displayHelp', Documentation.Workspace.InstallOnWindows); - return Promise.resolve(); + if (product === Product.ctags && settings.IS_WINDOWS) { + if (this.outputChannel) { + this.outputChannel.appendLine('Install Universal Ctags Win32 to enable support for Workspace Symbols'); + this.outputChannel.appendLine('Download the CTags binary from the Universal CTags site.'); + this.outputChannel.appendLine('Option 1: Extract ctags.exe from the downloaded zip to any folder within your PATH so that Visual Studio Code can run it.'); + this.outputChannel.appendLine('Option 2: Extract to any folder and add the path to this folder to the command setting.'); + this.outputChannel.appendLine('Option 3: Extract to any folder and define that path in the python.workspaceSymbols.ctagsPath setting of your user settings file (settings.json).'); + this.outputChannel.show(); + } + else { + window.showInformationMessage('Install Universal Ctags and set it in your path or define the path in your python.workspaceSymbols.ctagsPath settings'); + } + return InstallerResponse.Ignore; } let installArgs = ProductInstallScripts.get(product)!; @@ -241,19 +255,19 @@ export class Installer implements vscode.Disposable { installArgs.splice(2, 0, '--proxy'); } } + let installationPromise: Promise; if (this.outputChannel && installArgs[0] === '-m') { // Errors are just displayed to the user this.outputChannel.show(); - return execPythonFile(settings.PythonSettings.getInstance().pythonPath, installArgs, vscode.workspace.rootPath!, true, (data) => { - this.outputChannel!.append(data); - }); + installationPromise = execPythonFile(settings.PythonSettings.getInstance(resource).pythonPath, + installArgs, getCwdForInstallScript(resource), true, (data) => { this.outputChannel!.append(data); }); } else { // When using terminal get the fully qualitified path // Cuz people may launch vs code from terminal when they have activated the appropriate virtual env // Problem is terminal doesn't use the currently activated virtual env // Must have something to do with the process being launched in the terminal - return getFullyQualifiedPythonInterpreterPath() + installationPromise = getFullyQualifiedPythonInterpreterPath() .then(pythonPath => { let installScript = installArgs.join(' '); @@ -269,42 +283,68 @@ export class Installer implements vscode.Disposable { Installer.terminal!.show(false); }); } + + return installationPromise + .then(() => this.isInstalled(product)) + .then(isInstalled => isInstalled ? InstallerResponse.Installed : InstallerResponse.Ignore); } - isInstalled(product: Product): Promise { - return isProductInstalled(product); + public isInstalled(product: Product, resource?: Uri): Promise { + return isProductInstalled(product, resource); } - uninstall(product: Product): Promise { - return uninstallproduct(product); + public uninstall(product: Product, resource?: Uri): Promise { + return uninstallproduct(product, resource); + } + public disableLinter(product: Product, resource: Uri) { + if (resource && !workspace.getWorkspaceFolder(resource)) { + const settingToDisable = SettingToDisableProduct.get(product)!; + const pythonConfig = workspace.getConfiguration('python', resource); + return pythonConfig.update(settingToDisable, false, ConfigurationTarget.Workspace); + } + else { + const pythonConfig = workspace.getConfiguration('python'); + return pythonConfig.update('linting.enabledWithoutWorkspace', false, true); + } + } + private updateSetting(setting: string, value: any, resource?: Uri) { + if (resource && !workspace.getWorkspaceFolder(resource)) { + const pythonConfig = workspace.getConfiguration('python', resource); + return pythonConfig.update(setting, value, ConfigurationTarget.Workspace); + } + else { + const pythonConfig = workspace.getConfiguration('python'); + return pythonConfig.update(setting, value, true); + } } } -export function disableLinter(product: Product, global?: boolean) { - const pythonConfig = vscode.workspace.getConfiguration('python'); - const settingToDisable = SettingToDisableProduct.get(product)!; - if (vscode.workspace.rootPath) { - return pythonConfig.update(settingToDisable, false, global); +function getCwdForInstallScript(resource?: Uri) { + const workspaceFolder = workspace.getWorkspaceFolder(resource); + if (workspaceFolder) { + return workspaceFolder.uri.fsPath; } - else { - return pythonConfig.update('linting.enabledWithoutWorkspace', false, true); + if (Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) { + return workspace.workspaceFolders[0].uri.fsPath; } + return __dirname; } -async function isProductInstalled(product: Product): Promise { +async function isProductInstalled(product: Product, resource?: Uri): Promise { if (!ProductExecutableAndArgs.has(product)) { return; } const prodExec = ProductExecutableAndArgs.get(product)!; - return execPythonFile(prodExec.executable, prodExec.args.concat(['--version']), vscode.workspace.rootPath!, false) - .then(() => { - return true; - }).catch(reason => { - return !isNotInstalledError(reason); - }); + const cwd = getCwdForInstallScript(resource); + return execPythonFile(prodExec.executable, prodExec.args.concat(['--version']), cwd, false) + .then(() => true) + .catch(reason => !isNotInstalledError(reason)); } -function uninstallproduct(product: Product): Promise { +function uninstallproduct(product: Product, resource?: Uri): Promise { + if (!ProductUninstallScripts.has(product)) { + return Promise.resolve(); + } const uninstallArgs = ProductUninstallScripts.get(product)!; - return execPythonFile('python', uninstallArgs, vscode.workspace.rootPath!, false); -} + return execPythonFile('python', uninstallArgs, getCwdForInstallScript(resource), false); +} \ No newline at end of file diff --git a/src/client/formatters/baseFormatter.ts b/src/client/formatters/baseFormatter.ts index 3fcce0d76661..0beaa02e9291 100644 --- a/src/client/formatters/baseFormatter.ts +++ b/src/client/formatters/baseFormatter.ts @@ -61,14 +61,14 @@ export abstract class BaseFormatter { } return getTextEditsFromPatch(document.getText(), data[1]); }).catch(error => { - this.handleError(this.Id, command, error); + this.handleError(this.Id, command, error, document.uri); return []; }); vscode.window.setStatusBarMessage(`Formatting with ${this.Id}`, promise); return promise; } - protected handleError(expectedFileName: string, fileName: string, error: Error) { + protected handleError(expectedFileName: string, fileName: string, error: Error, resource?: Uri) { let customError = `Formatting with ${this.Id} failed.`; if (isNotInstalledError(error)) { @@ -84,7 +84,7 @@ export abstract class BaseFormatter { } else { customError += `\nYou could either install the '${this.Id}' formatter, turn it off or use another formatter.`; - this.installer.promptToInstall(this.product); + this.installer.promptToInstall(this.product, resource); } } diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts index b4cd6ca154ed..edb02ed2eb79 100644 --- a/src/client/linters/baseLinter.ts +++ b/src/client/linters/baseLinter.ts @@ -1,7 +1,7 @@ 'use strict'; import { execPythonFile } from './../common/utils'; import * as settings from './../common/configSettings'; -import { OutputChannel } from 'vscode'; +import { OutputChannel, Uri } from 'vscode'; import { Installer, Product } from '../common/installer'; import * as vscode from 'vscode'; import { ErrorHandler } from './errorHandlers/main'; @@ -135,12 +135,12 @@ export abstract class BaseLinter { let outputLines = data.split(/\r?\n/g); return this.parseLines(outputLines, regEx); }).catch(error => { - this.handleError(this.Id, command, error); + this.handleError(this.Id, command, error, document.uri); return []; }); } - protected handleError(expectedFileName: string, fileName: string, error: Error) { - this._errorHandler.handleError(expectedFileName, fileName, error); + protected handleError(expectedFileName: string, fileName: string, error: Error, resource?: Uri) { + this._errorHandler.handleError(expectedFileName, fileName, error, resource); } } diff --git a/src/client/linters/errorHandlers/invalidArgs.ts b/src/client/linters/errorHandlers/invalidArgs.ts index 514b2b82e523..b2d4b155cadb 100644 --- a/src/client/linters/errorHandlers/invalidArgs.ts +++ b/src/client/linters/errorHandlers/invalidArgs.ts @@ -1,6 +1,6 @@ 'use strict'; import { isNotInstalledError } from '../../common/helpers'; -import * as vscode from 'vscode'; +import { Uri, window } from 'vscode'; import { StandardErrorHandler } from './standard'; export class InvalidArgumentsErrorHandler extends StandardErrorHandler { @@ -13,14 +13,14 @@ export class InvalidArgumentsErrorHandler extends StandardErrorHandler { private displayInvalidArgsError() { // Ok if we have a space after the file name, this means we have some arguments defined and this isn't supported - vscode.window.showErrorMessage(`Unsupported configuration for '${this.id}'`, 'View Errors').then(item => { + window.showErrorMessage(`Unsupported configuration for '${this.id}'`, 'View Errors').then(item => { if (item === 'View Errors') { this.outputChannel.show(); } }); } - public handleError(expectedFileName: string, fileName: string, error: Error): boolean { + public handleError(expectedFileName: string, fileName: string, error: Error, resource?: Uri): boolean { if (!isNotInstalledError(error)) { return false; } diff --git a/src/client/linters/errorHandlers/main.ts b/src/client/linters/errorHandlers/main.ts index 9503e01e4a69..cc9120627929 100644 --- a/src/client/linters/errorHandlers/main.ts +++ b/src/client/linters/errorHandlers/main.ts @@ -1,5 +1,5 @@ 'use strict'; -import { OutputChannel } from 'vscode'; +import { OutputChannel, Uri } from 'vscode'; import { Installer, Product } from '../../common/installer'; import { InvalidArgumentsErrorHandler } from './invalidArgs'; import { StandardErrorHandler } from './standard'; @@ -15,7 +15,7 @@ export class ErrorHandler { ]; } - public handleError(expectedFileName: string, fileName: string, error: Error) { + public handleError(expectedFileName: string, fileName: string, error: Error, resource?: Uri) { this._errorHandlers.some(handler => handler.handleError(expectedFileName, fileName, error)); } } \ No newline at end of file diff --git a/src/client/linters/errorHandlers/notInstalled.ts b/src/client/linters/errorHandlers/notInstalled.ts index bc7c676a9390..f875997b184f 100644 --- a/src/client/linters/errorHandlers/notInstalled.ts +++ b/src/client/linters/errorHandlers/notInstalled.ts @@ -1,14 +1,15 @@ 'use strict'; +import { Uri } from 'vscode'; import { isNotInstalledError } from '../../common/helpers'; import { StandardErrorHandler } from './standard'; export class NotInstalledErrorHandler extends StandardErrorHandler { - public handleError(expectedFileName: string, fileName: string, error: Error): boolean { + public handleError(expectedFileName: string, fileName: string, error: Error, resource?: Uri): boolean { if (!isNotInstalledError(error)) { return false; } - this.installer.promptToInstall(this.product); + this.installer.promptToInstall(this.product, resource); const customError = `Linting with ${this.id} failed.\nYou could either install the '${this.id}' linter or turn it off in setings.json via "python.linting.${this.id}Enabled = false".`; this.outputChannel.appendLine(`\n${customError}\n${error + ''}`); return true; diff --git a/src/client/linters/errorHandlers/standard.ts b/src/client/linters/errorHandlers/standard.ts index 114d99e5c79b..e9f665a0d09c 100644 --- a/src/client/linters/errorHandlers/standard.ts +++ b/src/client/linters/errorHandlers/standard.ts @@ -1,18 +1,17 @@ 'use strict'; -import { OutputChannel } from 'vscode'; -import { Installer, Product, disableLinter } from '../../common/installer'; -import * as vscode from 'vscode'; +import { OutputChannel, Uri, window } from 'vscode'; +import { Installer, Product } from '../../common/installer'; export class StandardErrorHandler { constructor(protected id: string, protected product: Product, protected installer: Installer, protected outputChannel: OutputChannel) { } - private displayLinterError() { + private displayLinterError(resource?: Uri) { const message = `There was an error in running the linter '${this.id}'`; - vscode.window.showErrorMessage(message, 'Disable linter', 'View Errors').then(item => { + window.showErrorMessage(message, 'Disable linter', 'View Errors').then(item => { switch (item) { case 'Disable linter': { - disableLinter(this.product); + this.installer.disableLinter(this.product, resource); break; } case 'View Errors': { @@ -23,7 +22,7 @@ export class StandardErrorHandler { }); } - public handleError(expectedFileName: string, fileName: string, error: Error): boolean { + public handleError(expectedFileName: string, fileName: string, error: Error, resource?: Uri): boolean { if (typeof error === 'string' && (error as string).indexOf("OSError: [Errno 2] No such file or directory: '/") > 0) { return false; } @@ -31,7 +30,7 @@ export class StandardErrorHandler { console.error(error); this.outputChannel.appendLine(`Linting with ${this.id} failed.\n${error + ''}`); - this.displayLinterError(); + this.displayLinterError(resource); return true; } } diff --git a/src/client/linters/prospector.ts b/src/client/linters/prospector.ts index 42c14dfd76e2..6dc2ce4dcfb8 100644 --- a/src/client/linters/prospector.ts +++ b/src/client/linters/prospector.ts @@ -39,8 +39,8 @@ export class Linter extends baseLinter.BaseLinter { let prospectorPath = this.pythonSettings.linting.prospectorPath; let outputChannel = this.outputChannel; let prospectorArgs = Array.isArray(this.pythonSettings.linting.prospectorArgs) ? this.pythonSettings.linting.prospectorArgs : []; - - if (prospectorArgs.length === 0 && ProductExecutableAndArgs.has(Product.prospector) && prospectorPath.toLocaleLowerCase() === 'prospector'){ + + if (prospectorArgs.length === 0 && ProductExecutableAndArgs.has(Product.prospector) && prospectorPath.toLocaleLowerCase() === 'prospector') { prospectorPath = ProductExecutableAndArgs.get(Product.prospector).executable; prospectorArgs = ProductExecutableAndArgs.get(Product.prospector).args; } @@ -73,7 +73,7 @@ export class Linter extends baseLinter.BaseLinter { resolve(diagnostics); }).catch(error => { - this.handleError(this.Id, prospectorPath, error); + this.handleError(this.Id, prospectorPath, error, document.uri); resolve([]); }); }); diff --git a/src/client/linters/pydocstyle.ts b/src/client/linters/pydocstyle.ts index b361bf1640ec..1c5cc9b374b2 100644 --- a/src/client/linters/pydocstyle.ts +++ b/src/client/linters/pydocstyle.ts @@ -102,7 +102,7 @@ export class Linter extends baseLinter.BaseLinter { }); resolve(diagnostics); }, error => { - this.handleError(this.Id, commandLine, error); + this.handleError(this.Id, commandLine, error, document.uri); resolve([]); }); }); diff --git a/src/client/providers/renameProvider.ts b/src/client/providers/renameProvider.ts index 9afcb4db6c47..50205bfc75dc 100644 --- a/src/client/providers/renameProvider.ts +++ b/src/client/providers/renameProvider.ts @@ -48,7 +48,7 @@ export class PythonRenameProvider implements vscode.RenameProvider { return workspaceEdit; }).catch(reason => { if (reason === 'Not installed') { - this.installer.promptToInstall(Product.rope); + this.installer.promptToInstall(Product.rope, document.uri); return Promise.reject(''); } else { diff --git a/src/client/providers/simpleRefactorProvider.ts b/src/client/providers/simpleRefactorProvider.ts index 99e62ac55b5a..080db9518de6 100644 --- a/src/client/providers/simpleRefactorProvider.ts +++ b/src/client/providers/simpleRefactorProvider.ts @@ -127,7 +127,7 @@ function extractName(extensionDir: string, textEditor: vscode.TextEditor, range: } }).catch(error => { if (error === 'Not installed') { - installer.promptToInstall(Product.rope); + installer.promptToInstall(Product.rope, textEditor.document.uri); return Promise.reject(''); } let errorMessage = error + ''; diff --git a/src/client/unittests/common/baseTestManager.ts b/src/client/unittests/common/baseTestManager.ts index e6c31d31a0be..1dc38f7259b2 100644 --- a/src/client/unittests/common/baseTestManager.ts +++ b/src/client/unittests/common/baseTestManager.ts @@ -90,7 +90,7 @@ export abstract class BaseTestManager { return tests; }).catch(reason => { if (isNotInstalledError(reason) && !quietMode) { - this.installer.promptToInstall(this.product); + this.installer.promptToInstall(this.product, vscode.Uri.file(this.rootDirectory)); } this.tests = null; diff --git a/src/client/workspaceSymbols/main.ts b/src/client/workspaceSymbols/main.ts index 835c7cec545c..5c0206290d0e 100644 --- a/src/client/workspaceSymbols/main.ts +++ b/src/client/workspaceSymbols/main.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { Generator } from './generator'; -import { Product, Installer } from '../common/installer'; +import { Installer, InstallerResponse, Product } from '../common/installer'; import { PythonSettings } from '../common/configSettings'; import { fsExistsAsync } from '../common/utils'; import { isNotInstalledError } from '../common/helpers'; @@ -52,11 +52,6 @@ export class WorkspaceSymbols implements vscode.Disposable { dispose() { this.disposables.forEach(d => d.dispose()); } - disableDocumentLanguageProvider(): Thenable { - const pythonConfig = vscode.workspace.getConfiguration('python'); - return pythonConfig.update('python.workspaceSymbols.enabled', false); - - } buildWorkspaceSymbols(rebuild: boolean = true, token?: vscode.CancellationToken): Promise { if (!pythonSettings.workspaceSymbols.enabled || (token && token.isCancellationRequested)) { return Promise.resolve([]); @@ -81,23 +76,15 @@ export class WorkspaceSymbols implements vscode.Disposable { if (!token || token.isCancellationRequested) { return; } - return new Promise((resolve, reject) => { - vscode.window.showErrorMessage('CTags needs to be installed to get support for Python workspace symbols', - 'Install', `Don't ask again`).then(item => { - switch (item) { - case 'Install': { - this.installer.install(Product.ctags).then(() => { - return this.buildWorkspaceSymbols(rebuild, token); - }).catch(reason => reject(reason)); - break; - } - case `Don't ask again`: { - this.disableDocumentLanguageProvider().then(() => resolve(), reason => reject(reason)); - break; - } - } - }); - }); + return this.installer.promptToInstall(Product.ctags) + .then(result => { + if (!token || token.isCancellationRequested) { + return; + } + if (result === InstallerResponse.Installed) { + return this.buildWorkspaceSymbols(rebuild, token); + } + }); }); }); } diff --git a/src/test/autocomplete/base.test.ts b/src/test/autocomplete/base.test.ts index 609bed153456..83123b308f91 100644 --- a/src/test/autocomplete/base.test.ts +++ b/src/test/autocomplete/base.test.ts @@ -114,7 +114,7 @@ suite('Autocomplete', () => { const position = new vscode.Position(10, 9); const list = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', textDocument.uri, position); assert.notEqual(list.items.filter(item => item.label === 'sleep').length, 0, 'sleep not found'); - assert.notEqual(list.items.filter(item => item.documentation.startsWith("Delay execution for a given number of seconds. The argument may be")).length, 0, 'Documentation incorrect'); + assert.notEqual(list.items.filter(item => item.documentation.toString().startsWith("Delay execution for a given number of seconds. The argument may be")).length, 0, 'Documentation incorrect'); }); test('For custom class', done => { From 663b3455329e9c89ac9f614a08b31b818a4385b8 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 9 Oct 2017 13:54:07 -0700 Subject: [PATCH 10/57] null test in installer --- src/client/common/installer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/common/installer.ts b/src/client/common/installer.ts index 6d7af57228a5..8710273ee857 100644 --- a/src/client/common/installer.ts +++ b/src/client/common/installer.ts @@ -320,7 +320,7 @@ export class Installer implements vscode.Disposable { } function getCwdForInstallScript(resource?: Uri) { - const workspaceFolder = workspace.getWorkspaceFolder(resource); + const workspaceFolder = resource ? workspace.getWorkspaceFolder(resource) : undefined; if (workspaceFolder) { return workspaceFolder.uri.fsPath; } From bcca381e9e13085d47ac27e211ca6c1b3ae8b53c Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 9 Oct 2017 14:10:02 -0700 Subject: [PATCH 11/57] canges to config settings to support multiroot workspace --- src/client/common/configSettings.ts | 2 +- src/client/common/systemVariables.ts | 5 ++--- src/test/common/configSettings.test.ts | 4 +++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index 32ef9ec060fb..a7d013a1d68f 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -160,8 +160,8 @@ export class PythonSettings extends EventEmitter implements IPythonSettings { return PythonSettings.pythonSettings.get(workspaceFolderKey); } private initializeSettings() { - const systemVariables: SystemVariables = new SystemVariables(); const workspaceRoot = (IS_TEST_EXECUTION || !this.workspaceRoot) ? __dirname : this.workspaceRoot.fsPath; + const systemVariables: SystemVariables = new SystemVariables(this.workspaceRoot ? this.workspaceRoot.fsPath : undefined); const pythonSettings = vscode.workspace.getConfiguration('python', this.workspaceRoot); this.pythonPath = systemVariables.resolveAny(pythonSettings.get('pythonPath'))!; this.pythonPath = getAbsolutePath(this.pythonPath, IS_TEST_EXECUTION ? __dirname : workspaceRoot); diff --git a/src/client/common/systemVariables.ts b/src/client/common/systemVariables.ts index f0df0218c2d9..e4adcaf67862 100644 --- a/src/client/common/systemVariables.ts +++ b/src/client/common/systemVariables.ts @@ -133,11 +133,10 @@ export abstract class AbstractSystemVariables implements ISystemVariables { export class SystemVariables extends AbstractSystemVariables { private _workspaceRoot: string; private _workspaceRootFolderName: string; - private _execPath: string; - constructor() { + constructor(workspaceRoot?: string) { super(); - this._workspaceRoot = typeof vscode.workspace.rootPath === 'string' ? vscode.workspace.rootPath : __dirname; + this._workspaceRoot = typeof workspaceRoot === 'string' ? vscode.workspace.rootPath : __dirname; this._workspaceRootFolderName = Path.basename(this._workspaceRoot); Object.keys(process.env).forEach(key => { this[`env:${key}`] = this[`env.${key}`] = process.env[key]; diff --git a/src/test/common/configSettings.test.ts b/src/test/common/configSettings.test.ts index 8382b8f4e1c9..07cfe597033a 100644 --- a/src/test/common/configSettings.test.ts +++ b/src/test/common/configSettings.test.ts @@ -9,11 +9,13 @@ import * as assert from 'assert'; // You can import and use all API from the 'vscode' module // as well as import your extension to test it import * as vscode from 'vscode'; +import * as path from 'path'; import { initialize, IS_TRAVIS } from './../initialize'; import { PythonSettings } from '../../client/common/configSettings'; import { SystemVariables } from '../../client/common/systemVariables'; const pythonSettings = PythonSettings.getInstance(); +const workspaceRoot = path.join(__dirname, '..', '..', '..', 'src', 'test'); // Defines a Mocha test suite to group tests of similar kind together suite('Configuration Settings', () => { @@ -21,7 +23,7 @@ suite('Configuration Settings', () => { if (!IS_TRAVIS) { test('Check Values', done => { - const systemVariables: SystemVariables = new SystemVariables(); + const systemVariables: SystemVariables = new SystemVariables(workspaceRoot); const pythonConfig = vscode.workspace.getConfiguration('python'); Object.keys(pythonSettings).forEach(key => { let settingValue = pythonConfig.get(key, 'Not a config'); From e3279cbcc197c3b4a22207cb6b9f5fc387f28e35 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 9 Oct 2017 14:13:08 -0700 Subject: [PATCH 12/57] changes to code refactoring to support workspace symbols --- src/client/common/editor.ts | 4 ++-- src/client/providers/renameProvider.ts | 15 ++++++++----- .../providers/simpleRefactorProvider.ts | 22 ++++++++++++++----- src/client/refactor/proxy.ts | 2 +- src/test/index.ts | 3 ++- .../extension.refactor.extract.method.test.ts | 2 +- .../extension.refactor.extract.var.test.ts | 2 +- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/client/common/editor.ts b/src/client/common/editor.ts index 9d23d38ff678..7da7a2657e8b 100644 --- a/src/client/common/editor.ts +++ b/src/client/common/editor.ts @@ -75,7 +75,7 @@ export function getTextEditsFromPatch(before: string, patch: string): TextEdit[] return textEdits; } -export function getWorkspaceEditsFromPatch(filePatches: string[]): WorkspaceEdit { +export function getWorkspaceEditsFromPatch(filePatches: string[], workspaceRoot?:string): WorkspaceEdit { const workspaceEdit = new WorkspaceEdit(); filePatches.forEach(patch => { const indexOfAtAt = patch.indexOf('@@'); @@ -101,7 +101,7 @@ export function getWorkspaceEditsFromPatch(filePatches: string[]): WorkspaceEdit } let fileName = fileNameLines[0].substring(fileNameLines[0].indexOf(' a') + 3).trim(); - fileName = path.isAbsolute(fileName) ? fileName : path.resolve(vscode.workspace.rootPath, fileName); + fileName = workspaceRoot && !path.isAbsolute(fileName) ? path.resolve(workspaceRoot, fileName) : fileName; if (!fs.existsSync(fileName)) { return; } diff --git a/src/client/providers/renameProvider.ts b/src/client/providers/renameProvider.ts index 50205bfc75dc..e65b915b856b 100644 --- a/src/client/providers/renameProvider.ts +++ b/src/client/providers/renameProvider.ts @@ -7,7 +7,6 @@ import * as path from 'path'; import { PythonSettings } from '../common/configSettings'; import { Installer, Product } from '../common/installer'; -const pythonSettings = PythonSettings.getInstance(); const EXTENSION_DIR = path.join(__dirname, '..', '..', '..'); interface RenameResponse { results: [{ diff: string }]; @@ -41,11 +40,17 @@ export class PythonRenameProvider implements vscode.RenameProvider { return; } - let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, vscode.workspace.rootPath); + let workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); + if (!workspaceFolder && Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0) { + workspaceFolder = vscode.workspace.workspaceFolders[0]; + } + const workspaceRoot = workspaceFolder ? workspaceFolder.uri.fsPath : __dirname; + const pythonSettings = PythonSettings.getInstance(workspaceFolder ? workspaceFolder.uri : undefined); + + let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, workspaceRoot); return proxy.rename(document, newName, document.uri.fsPath, range).then(response => { - //return response.results[0].diff; - const workspaceEdit = getWorkspaceEditsFromPatch(response.results.map(fileChanges => fileChanges.diff)); - return workspaceEdit; + const fileDiffs = response.results.map(fileChanges => fileChanges.diff); + return getWorkspaceEditsFromPatch(fileDiffs, workspaceRoot); }).catch(reason => { if (reason === 'Not installed') { this.installer.promptToInstall(Product.rope, document.uri); diff --git a/src/client/providers/simpleRefactorProvider.ts b/src/client/providers/simpleRefactorProvider.ts index 080db9518de6..35f765165eb1 100644 --- a/src/client/providers/simpleRefactorProvider.ts +++ b/src/client/providers/simpleRefactorProvider.ts @@ -3,7 +3,7 @@ import * as vscode from 'vscode'; import { RefactorProxy } from '../refactor/proxy'; import { getTextEditsFromPatch } from '../common/editor'; -import { PythonSettings, IPythonSettings } from '../common/configSettings'; +import { PythonSettings } from '../common/configSettings'; import { Installer, Product } from '../common/installer'; interface RenameResponse { @@ -34,8 +34,14 @@ export function activateSimplePythonRefactorProvider(context: vscode.ExtensionCo // Exported for unit testing export function extractVariable(extensionDir: string, textEditor: vscode.TextEditor, range: vscode.Range, - outputChannel: vscode.OutputChannel, workspaceRoot: string = vscode.workspace.rootPath, - pythonSettings: IPythonSettings = PythonSettings.getInstance()): Promise { + outputChannel: vscode.OutputChannel): Promise { + + let workspaceFolder = vscode.workspace.getWorkspaceFolder(textEditor.document.uri); + if (!workspaceFolder && Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0) { + workspaceFolder = vscode.workspace.workspaceFolders[0]; + } + const workspaceRoot = workspaceFolder ? workspaceFolder.uri.fsPath : __dirname; + const pythonSettings = PythonSettings.getInstance(workspaceFolder ? workspaceFolder.uri : undefined); return validateDocumentForRefactor(textEditor).then(() => { let newName = 'newvariable' + new Date().getMilliseconds().toString(); @@ -50,8 +56,14 @@ export function extractVariable(extensionDir: string, textEditor: vscode.TextEdi // Exported for unit testing export function extractMethod(extensionDir: string, textEditor: vscode.TextEditor, range: vscode.Range, - outputChannel: vscode.OutputChannel, workspaceRoot: string = vscode.workspace.rootPath, - pythonSettings: IPythonSettings = PythonSettings.getInstance()): Promise { + outputChannel: vscode.OutputChannel): Promise { + + let workspaceFolder = vscode.workspace.getWorkspaceFolder(textEditor.document.uri); + if (!workspaceFolder && Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0) { + workspaceFolder = vscode.workspace.workspaceFolders[0]; + } + const workspaceRoot = workspaceFolder ? workspaceFolder.uri.fsPath : __dirname; + const pythonSettings = PythonSettings.getInstance(workspaceFolder ? workspaceFolder.uri : undefined); return validateDocumentForRefactor(textEditor).then(() => { let newName = 'newmethod' + new Date().getMilliseconds().toString(); diff --git a/src/client/refactor/proxy.ts b/src/client/refactor/proxy.ts index 41a717036754..aada24c2323d 100644 --- a/src/client/refactor/proxy.ts +++ b/src/client/refactor/proxy.ts @@ -17,7 +17,7 @@ export class RefactorProxy extends vscode.Disposable { private _commandResolve: (value?: any | PromiseLike) => void; private _commandReject: (reason?: any) => void; private _initializeReject: (reason?: any) => void; - constructor(extensionDir: string, private pythonSettings: IPythonSettings, private workspaceRoot: string = vscode.workspace.rootPath) { + constructor(extensionDir: string, private pythonSettings: IPythonSettings, private workspaceRoot: string) { super(() => { }); this._extensionDir = extensionDir; } diff --git a/src/test/index.ts b/src/test/index.ts index af916a981db9..28acd887d255 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -18,7 +18,8 @@ let testRunner = require('vscode/lib/testrunner'); testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results - timeout: 25000 + timeout: 25000, + grep: 'Configuration Settings' }); initializePython(); diff --git a/src/test/refactor/extension.refactor.extract.method.test.ts b/src/test/refactor/extension.refactor.extract.method.test.ts index f3cd520f608c..021bb90532ff 100644 --- a/src/test/refactor/extension.refactor.extract.method.test.ts +++ b/src/test/refactor/extension.refactor.extract.method.test.ts @@ -119,7 +119,7 @@ suite('Method Extraction', () => { textEditor = editor; return; }).then(() => { - return extractMethod(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch, path.dirname(refactorTargetFile), pythonSettings).then(() => { + return extractMethod(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch).then(() => { if (shouldError) { ignoreErrorHandling = true; assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); diff --git a/src/test/refactor/extension.refactor.extract.var.test.ts b/src/test/refactor/extension.refactor.extract.var.test.ts index 7428a6b1d43d..1e2a896a4e56 100644 --- a/src/test/refactor/extension.refactor.extract.var.test.ts +++ b/src/test/refactor/extension.refactor.extract.var.test.ts @@ -117,7 +117,7 @@ suite('Variable Extraction', () => { textEditor = editor; return; }).then(() => { - return extractVariable(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch, path.dirname(refactorTargetFile), pythonSettings).then(() => { + return extractVariable(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch).then(() => { if (shouldError) { ignoreErrorHandling = true; assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); From 965eae9272a03a280f3f2ce066c6f7f46cea9dc8 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 9 Oct 2017 14:16:34 -0700 Subject: [PATCH 13/57] oops --- src/test/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index 28acd887d255..af916a981db9 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -18,8 +18,7 @@ let testRunner = require('vscode/lib/testrunner'); testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results - timeout: 25000, - grep: 'Configuration Settings' + timeout: 25000 }); initializePython(); From 234ff785a35268818078ff2acbfd334474edf363 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 9 Oct 2017 15:16:24 -0700 Subject: [PATCH 14/57] modified to settings are resolved using document uri --- src/client/linters/baseLinter.ts | 16 ++++++----- src/client/linters/flake8.ts | 5 +--- src/client/linters/mypy.ts | 5 +--- src/client/linters/pep8Linter.ts | 11 +++----- src/client/linters/prospector.ts | 5 +--- src/client/linters/pydocstyle.ts | 5 +--- src/client/linters/pylama.ts | 5 +--- src/client/linters/pylint.ts | 5 +--- src/client/providers/lintProvider.ts | 40 ++++++++++------------------ src/test/linters/lint.test.ts | 29 +++++++++++++++----- 10 files changed, 57 insertions(+), 69 deletions(-) diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts index b4cd6ca154ed..9e1dcd8a54f5 100644 --- a/src/client/linters/baseLinter.ts +++ b/src/client/linters/baseLinter.ts @@ -1,6 +1,6 @@ 'use strict'; +import { IPythonSettings, PythonSettings } from '../common/configSettings'; import { execPythonFile } from './../common/utils'; -import * as settings from './../common/configSettings'; import { OutputChannel } from 'vscode'; import { Installer, Product } from '../common/installer'; import * as vscode from 'vscode'; @@ -49,9 +49,12 @@ export function matchNamedRegEx(data, regex): IRegexGroup { export abstract class BaseLinter { public Id: string; - protected pythonSettings: settings.IPythonSettings; protected _columnOffset = 0; private _errorHandler: ErrorHandler; + private _pythonSettings: IPythonSettings; + protected get pythonSettings(): IPythonSettings { + return this._pythonSettings; + } protected getWorkspaceRootPath(document: vscode.TextDocument): string { const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); const workspaceRootPath = (workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string') ? workspaceFolder.uri.fsPath : undefined; @@ -59,12 +62,13 @@ export abstract class BaseLinter { } constructor(id: string, public product: Product, protected outputChannel: OutputChannel) { this.Id = id; - this.pythonSettings = settings.PythonSettings.getInstance(); this._errorHandler = new ErrorHandler(this.Id, product, new Installer(), this.outputChannel); } - public abstract isEnabled(): Boolean; - public abstract runLinter(document: vscode.TextDocument, cancellation: vscode.CancellationToken): Promise; - + public lint(document: vscode.TextDocument, cancellation: vscode.CancellationToken): Promise { + this._pythonSettings = PythonSettings.getInstance(document.uri); + return this.runLinter(document, cancellation); + } + protected abstract runLinter(document: vscode.TextDocument, cancellation: vscode.CancellationToken): Promise; protected parseMessagesSeverity(error: string, categorySeverity: any): LintMessageSeverity { if (categorySeverity[error]) { let severityName = categorySeverity[error]; diff --git a/src/client/linters/flake8.ts b/src/client/linters/flake8.ts index b141b3a3a86c..00bca18c28d4 100644 --- a/src/client/linters/flake8.ts +++ b/src/client/linters/flake8.ts @@ -12,10 +12,7 @@ export class Linter extends baseLinter.BaseLinter { super('flake8', Product.flake8, outputChannel); } - public isEnabled(): Boolean { - return this.pythonSettings.linting.flake8Enabled; - } - public runLinter(document: TextDocument, cancellation: CancellationToken): Promise { + protected runLinter(document: TextDocument, cancellation: CancellationToken): Promise { if (!this.pythonSettings.linting.flake8Enabled) { return Promise.resolve([]); } diff --git a/src/client/linters/mypy.ts b/src/client/linters/mypy.ts index 407c218bc08f..8696d4db5a78 100644 --- a/src/client/linters/mypy.ts +++ b/src/client/linters/mypy.ts @@ -12,10 +12,7 @@ export class Linter extends baseLinter.BaseLinter { super('mypy', Product.mypy, outputChannel); } - public isEnabled(): Boolean { - return this.pythonSettings.linting.mypyEnabled; - } - public runLinter(document: TextDocument, cancellation: CancellationToken): Promise { + protected runLinter(document: TextDocument, cancellation: CancellationToken): Promise { if (!this.pythonSettings.linting.mypyEnabled) { return Promise.resolve([]); } diff --git a/src/client/linters/pep8Linter.ts b/src/client/linters/pep8Linter.ts index bb42f3c30834..18f77e4bce54 100644 --- a/src/client/linters/pep8Linter.ts +++ b/src/client/linters/pep8Linter.ts @@ -7,23 +7,20 @@ import { TextDocument, CancellationToken } from 'vscode'; export class Linter extends baseLinter.BaseLinter { _columnOffset = 1; - + constructor(outputChannel: OutputChannel) { super('pep8', Product.pep8, outputChannel); } - public isEnabled(): Boolean { - return this.pythonSettings.linting.pep8Enabled; - } - public runLinter(document: TextDocument, cancellation: CancellationToken): Promise { + protected runLinter(document: TextDocument, cancellation: CancellationToken): Promise { if (!this.pythonSettings.linting.pep8Enabled) { return Promise.resolve([]); } let pep8Path = this.pythonSettings.linting.pep8Path; let pep8Args = Array.isArray(this.pythonSettings.linting.pep8Args) ? this.pythonSettings.linting.pep8Args : []; - - if (pep8Args.length === 0 && ProductExecutableAndArgs.has(Product.pep8) && pep8Path.toLocaleLowerCase() === 'pep8'){ + + if (pep8Args.length === 0 && ProductExecutableAndArgs.has(Product.pep8) && pep8Path.toLocaleLowerCase() === 'pep8') { pep8Path = ProductExecutableAndArgs.get(Product.pep8).executable; pep8Args = ProductExecutableAndArgs.get(Product.pep8).args; } diff --git a/src/client/linters/prospector.ts b/src/client/linters/prospector.ts index 42c14dfd76e2..e02a10f210be 100644 --- a/src/client/linters/prospector.ts +++ b/src/client/linters/prospector.ts @@ -28,10 +28,7 @@ export class Linter extends baseLinter.BaseLinter { super('prospector', Product.prospector, outputChannel); } - public isEnabled(): Boolean { - return this.pythonSettings.linting.prospectorEnabled; - } - public runLinter(document: TextDocument, cancellation: CancellationToken): Promise { + protected runLinter(document: TextDocument, cancellation: CancellationToken): Promise { if (!this.pythonSettings.linting.prospectorEnabled) { return Promise.resolve([]); } diff --git a/src/client/linters/pydocstyle.ts b/src/client/linters/pydocstyle.ts index b361bf1640ec..e58fa4edd5a5 100644 --- a/src/client/linters/pydocstyle.ts +++ b/src/client/linters/pydocstyle.ts @@ -13,10 +13,7 @@ export class Linter extends baseLinter.BaseLinter { super('pydocstyle', Product.pydocstyle, outputChannel); } - public isEnabled(): Boolean { - return this.pythonSettings.linting.pydocstyleEnabled; - } - public runLinter(document: TextDocument, cancellation: CancellationToken): Promise { + protected runLinter(document: TextDocument, cancellation: CancellationToken): Promise { if (!this.pythonSettings.linting.pydocstyleEnabled) { return Promise.resolve([]); } diff --git a/src/client/linters/pylama.ts b/src/client/linters/pylama.ts index 718ec652adf4..f0251ee728ea 100644 --- a/src/client/linters/pylama.ts +++ b/src/client/linters/pylama.ts @@ -14,10 +14,7 @@ export class Linter extends baseLinter.BaseLinter { super('pylama', Product.pylama, outputChannel); } - public isEnabled(): Boolean { - return this.pythonSettings.linting.pylamaEnabled; - } - public runLinter(document: TextDocument, cancellation: CancellationToken): Promise { + protected runLinter(document: TextDocument, cancellation: CancellationToken): Promise { if (!this.pythonSettings.linting.pylamaEnabled) { return Promise.resolve([]); } diff --git a/src/client/linters/pylint.ts b/src/client/linters/pylint.ts index d485f7764ba1..5649d079f008 100644 --- a/src/client/linters/pylint.ts +++ b/src/client/linters/pylint.ts @@ -10,10 +10,7 @@ export class Linter extends baseLinter.BaseLinter { super('pylint', Product.pylint, outputChannel); } - public isEnabled(): Boolean { - return this.pythonSettings.linting.pylintEnabled; - } - public runLinter(document: TextDocument, cancellation: CancellationToken): Promise { + protected runLinter(document: TextDocument, cancellation: CancellationToken): Promise { if (!this.pythonSettings.linting.pylintEnabled) { return Promise.resolve([]); } diff --git a/src/client/providers/lintProvider.ts b/src/client/providers/lintProvider.ts index 165055bfccc8..b9b5b92107a7 100644 --- a/src/client/providers/lintProvider.ts +++ b/src/client/providers/lintProvider.ts @@ -10,7 +10,7 @@ import * as pylama from './../linters/pylama'; import * as flake8 from './../linters/flake8'; import * as pydocstyle from './../linters/pydocstyle'; import * as mypy from './../linters/mypy'; -import * as settings from '../common/configSettings'; +import { PythonSettings } from '../common/configSettings'; import * as fs from 'fs'; import { LinterErrors } from '../common/constants'; const Minimatch = require("minimatch").Minimatch; @@ -37,37 +37,23 @@ interface DocumentHasJupyterCodeCells { (doc: vscode.TextDocument, token: vscode.CancellationToken): Promise; } export class LintProvider extends vscode.Disposable { - private settings: settings.IPythonSettings; private diagnosticCollection: vscode.DiagnosticCollection; private linters: linter.BaseLinter[] = []; private pendingLintings = new Map(); private outputChannel: vscode.OutputChannel; private context: vscode.ExtensionContext; private disposables: vscode.Disposable[]; - private ignoreMinmatches: { match: (fname: string) => boolean }[]; public constructor(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel, public documentHasJupyterCodeCells: DocumentHasJupyterCodeCells) { super(() => { }); this.outputChannel = outputChannel; this.context = context; - this.settings = settings.PythonSettings.getInstance(); this.disposables = []; - this.ignoreMinmatches = []; this.initialize(); - - this.disposables.push(vscode.workspace.onDidChangeConfiguration(this.onConfigChanged.bind(this))); } dispose() { this.disposables.forEach(d => d.dispose()); } - private onConfigChanged() { - this.initializeGlobs(); - } - private initializeGlobs() { - this.ignoreMinmatches = settings.PythonSettings.getInstance().linting.ignorePatterns.map(pattern => { - return new Minimatch(pattern); - }); - } private isDocumentOpen(uri: vscode.Uri): boolean { return vscode.window.visibleTextEditors.some(editor => editor.document && editor.document.uri.fsPath === uri.fsPath); } @@ -84,7 +70,8 @@ export class LintProvider extends vscode.Disposable { this.linters.push(new mypy.Linter(this.outputChannel)); let disposable = vscode.workspace.onDidSaveTextDocument((e) => { - if (e.languageId !== 'python' || !this.settings.linting.enabled || !this.settings.linting.lintOnSave) { + const settings = PythonSettings.getInstance(e.uri); + if (e.languageId !== 'python' || !settings.linting.enabled || !settings.linting.lintOnSave) { return; } this.lintDocument(e, 100); @@ -92,7 +79,8 @@ export class LintProvider extends vscode.Disposable { this.context.subscriptions.push(disposable); vscode.workspace.onDidOpenTextDocument((e) => { - if (e.languageId !== 'python' || !this.settings.linting.enabled) { + const settings = PythonSettings.getInstance(e.uri); + if (e.languageId !== 'python' || !settings.linting.enabled) { return; } // Exclude files opened by vscode when showing a diff view @@ -116,7 +104,6 @@ export class LintProvider extends vscode.Disposable { } }); this.context.subscriptions.push(disposable); - this.initializeGlobs(); } private lastTimeout: number; @@ -138,7 +125,12 @@ export class LintProvider extends vscode.Disposable { const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); const workspaceRootPath = (workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string') ? workspaceFolder.uri.fsPath : undefined; const relativeFileName = typeof workspaceRootPath === 'string' ? path.relative(workspaceRootPath, document.fileName) : document.fileName; - if (this.ignoreMinmatches.some(matcher => matcher.match(document.fileName) || matcher.match(relativeFileName))) { + const settings = PythonSettings.getInstance(document.uri); + const ignoreMinmatches = settings.linting.ignorePatterns.map(pattern => { + return new Minimatch(pattern); + }); + + if (ignoreMinmatches.some(matcher => matcher.match(document.fileName) || matcher.match(relativeFileName))) { return; } if (this.pendingLintings.has(document.uri.fsPath)) { @@ -156,14 +148,10 @@ export class LintProvider extends vscode.Disposable { this.pendingLintings.set(document.uri.fsPath, cancelToken); this.outputChannel.clear(); let promises: Promise[] = this.linters.map(linter => { - if (typeof workspaceRootPath !== 'string' && !this.settings.linting.enabledWithoutWorkspace) { - return Promise.resolve([]); - } - if (!linter.isEnabled()) { + if (typeof workspaceRootPath !== 'string' && !settings.linting.enabledWithoutWorkspace) { return Promise.resolve([]); } - // turn off telemetry for linters (at least for now) - return linter.runLinter(document, cancelToken.token); + return linter.lint(document, cancelToken.token); }); this.documentHasJupyterCodeCells(document, cancelToken.token).then(hasJupyterCodeCells => { // linters will resolve asynchronously - keep a track of all @@ -189,7 +177,7 @@ export class LintProvider extends vscode.Disposable { }); // Limit the number of messages to the max value - diagnostics = diagnostics.filter((value, index) => index <= this.settings.linting.maxNumberOfProblems); + diagnostics = diagnostics.filter((value, index) => index <= settings.linting.maxNumberOfProblems); if (!this.isDocumentOpen(document.uri)) { diagnostics = []; diff --git a/src/test/linters/lint.test.ts b/src/test/linters/lint.test.ts index 45657a478476..bf96de28cfc3 100644 --- a/src/test/linters/lint.test.ts +++ b/src/test/linters/lint.test.ts @@ -141,14 +141,31 @@ suite('Linting', () => { pythonSettings.linting.pydocstyleEnabled = true; }); suiteTeardown(() => closeActiveWindows()); - teardown(() => closeActiveWindows()); + teardown(() => { + closeActiveWindows(); + pythonSettings.linting.lintOnSave = false; + pythonSettings.linting.lintOnTextChange = false; + pythonSettings.linting.enabled = true; + pythonSettings.linting.pylintEnabled = true; + pythonSettings.linting.flake8Enabled = true; + pythonSettings.linting.pep8Enabled = true; + pythonSettings.linting.prospectorEnabled = true; + pythonSettings.linting.pydocstyleEnabled = true; + }); - function testEnablingDisablingOfLinter(linter: baseLinter.BaseLinter, propertyName: string) { + async function testEnablingDisablingOfLinter(linter: baseLinter.BaseLinter, propertyName: string) { pythonSettings.linting[propertyName] = true; - assert.equal(true, linter.isEnabled()); - pythonSettings.linting[propertyName] = false; - assert.equal(false, linter.isEnabled()); + let cancelToken = new vscode.CancellationTokenSource(); + disableAllButThisLinter(linter.product); + const document = await vscode.workspace.openTextDocument(fileToLint); + const editor = await vscode.window.showTextDocument(document); + try { + const messages = await linter.lint(editor.document, cancelToken.token); + assert.equal(messages.length, 0, 'Errors returned even when linter is disabled'); + } catch (error) { + assert.fail(error, null, 'Linter error'); + } } test('Enable and Disable Pylint', () => { let ch = new MockOutputChannel('Lint'); @@ -187,7 +204,7 @@ suite('Linting', () => { return vscode.workspace.openTextDocument(pythonFile) .then(document => vscode.window.showTextDocument(document)) .then(editor => { - return linter.runLinter(editor.document, cancelToken.token); + return linter.lint(editor.document, cancelToken.token); }) .then(messages => { // Different versions of python return different errors, From da4e2ab8eef428907dda2cd4b352b8eb2bf71340 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 10 Oct 2017 17:35:55 -0700 Subject: [PATCH 15/57] unit tests for multi root support --- .vscode/launch.json | 17 ++ .vscode/settings.json | 3 +- package.json | 4 +- src/client/common/configSettings.ts | 12 +- src/client/common/logger.ts | 4 +- .../common/configSettings.multiroot.test.ts | 165 ++++++++++++++++++ src/test/index.ts | 9 +- src/test/initialize.ts | 4 + src/test/linters/lint.multiroot.test.ts | 80 +++++++++ src/test/multiRootTest.ts | 8 + .../multiRootWkspc/disableLinters/file.py | 87 +++++++++ src/test/multiRootWkspc/multi.code-workspace | 22 +++ src/test/multiRootWkspc/parent/child/file.py | 87 +++++++++ .../workspace1/.vscode/settings.json | 5 + src/test/multiRootWkspc/workspace1/file.py | 87 +++++++++ 15 files changed, 583 insertions(+), 11 deletions(-) create mode 100644 src/test/common/configSettings.multiroot.test.ts create mode 100644 src/test/linters/lint.multiroot.test.ts create mode 100644 src/test/multiRootTest.ts create mode 100644 src/test/multiRootWkspc/disableLinters/file.py create mode 100644 src/test/multiRootWkspc/multi.code-workspace create mode 100644 src/test/multiRootWkspc/parent/child/file.py create mode 100644 src/test/multiRootWkspc/workspace1/.vscode/settings.json create mode 100644 src/test/multiRootWkspc/workspace1/file.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 284cb90bbaa5..6d4317be09b1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -67,6 +67,23 @@ ], "preLaunchTask": "npm" }, + { + "name": "Launch Multiroot Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceRoot}/src/test/multiRootWkspc/multi.code-workspace", + "--extensionDevelopmentPath=${workspaceRoot}", + "--extensionTestsPath=${workspaceRoot}/out/test" + ], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ], + "preLaunchTask": "npm" + }, { "name": "Python", "type": "python", diff --git a/.vscode/settings.json b/.vscode/settings.json index 2dc22387c78a..a09d3f11e14e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,8 @@ "out": false, // set this to true to hide the "out" folder with the compiled JS files "**/*.pyc": true, "**/__pycache__": true, - "node_modules": true + "node_modules": true, + ".vscode-test":true }, "search.exclude": { "out": true // set this to false to include "out" folder in search results diff --git a/package.json b/package.json index e19878709162..cb764423755b 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "theme": "dark" }, "engines": { - "vscode": "^1.15.0" + "vscode": "^1.17.0" }, "recommendations": [ "donjayamanne.jupyter" @@ -1594,7 +1594,7 @@ "vscode:prepublish": "tsc -p ./ && webpack", "compile": "webpack && tsc -watch -p ./", "postinstall": "node ./node_modules/vscode/bin/install", - "test": "node ./node_modules/vscode/bin/test" + "test": "node ./node_modules/vscode/bin/test && node ./out/test/multiRootTest.js" }, "dependencies": { "anser": "^1.1.0", diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index a7d013a1d68f..97222940b071 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -146,6 +146,12 @@ export class PythonSettings extends EventEmitter implements IPythonSettings { this.initializeSettings(); } + public static dispose() { + if (!IS_TEST_EXECUTION) { + throw new Error('Dispose can only be called from unit tests'); + } + PythonSettings.pythonSettings.clear(); + } public static getInstance(resource?: Uri): PythonSettings { const workspaceFolder = resource ? vscode.workspace.getWorkspaceFolder(resource) : undefined; let workspaceFolderUri: Uri = workspaceFolder ? workspaceFolder.uri : undefined; @@ -160,15 +166,15 @@ export class PythonSettings extends EventEmitter implements IPythonSettings { return PythonSettings.pythonSettings.get(workspaceFolderKey); } private initializeSettings() { - const workspaceRoot = (IS_TEST_EXECUTION || !this.workspaceRoot) ? __dirname : this.workspaceRoot.fsPath; + const workspaceRoot = this.workspaceRoot.fsPath; const systemVariables: SystemVariables = new SystemVariables(this.workspaceRoot ? this.workspaceRoot.fsPath : undefined); const pythonSettings = vscode.workspace.getConfiguration('python', this.workspaceRoot); this.pythonPath = systemVariables.resolveAny(pythonSettings.get('pythonPath'))!; - this.pythonPath = getAbsolutePath(this.pythonPath, IS_TEST_EXECUTION ? __dirname : workspaceRoot); + this.pythonPath = getAbsolutePath(this.pythonPath, workspaceRoot); this.venvPath = systemVariables.resolveAny(pythonSettings.get('venvPath'))!; this.jediPath = systemVariables.resolveAny(pythonSettings.get('jediPath'))!; if (typeof this.jediPath === 'string' && this.jediPath.length > 0) { - this.jediPath = getAbsolutePath(systemVariables.resolveAny(this.jediPath), IS_TEST_EXECUTION ? __dirname : workspaceRoot); + this.jediPath = getAbsolutePath(systemVariables.resolveAny(this.jediPath), workspaceRoot); } else { this.jediPath = ''; diff --git a/src/client/common/logger.ts b/src/client/common/logger.ts index ac372199e258..173da70d78ab 100644 --- a/src/client/common/logger.ts +++ b/src/client/common/logger.ts @@ -22,7 +22,9 @@ class Logger { Logger.writeLine(category, message); } static writeLine(category: string = "log", line: any) { - console[category](line); + if (process.env['PYTHON_DONJAYAMANNE_TEST'] !== '1') { + console[category](line); + } if (outChannel) { outChannel.appendLine(line); } diff --git a/src/test/common/configSettings.multiroot.test.ts b/src/test/common/configSettings.multiroot.test.ts new file mode 100644 index 000000000000..b40b92ec6308 --- /dev/null +++ b/src/test/common/configSettings.multiroot.test.ts @@ -0,0 +1,165 @@ +import * as assert from 'assert'; +import * as path from 'path'; +import { ConfigurationTarget, Uri, workspace } from 'vscode'; +import { initialize, closeActiveWindows } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; + +const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); + +suite('Multiroot Config Settings', () => { + suiteSetup(async () => { + await initialize(); + await resetSettings(); + }); + suiteTeardown(() => closeActiveWindows()); + teardown(async () => { + await resetSettings(); + // await closeActiveWindows(); + }); + + async function resetSettings() { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + let settings = workspace.getConfiguration('python', workspaceUri); + let value = settings.inspect('pythonPath'); + if (value.workspaceValue && value.workspaceValue !== 'python') { + await settings.update('pythonPath', undefined, ConfigurationTarget.Workspace); + } + if (value.workspaceFolderValue && value.workspaceFolderValue !== 'python') { + await settings.update('pythonPath', undefined, ConfigurationTarget.WorkspaceFolder); + } + PythonSettings.dispose(); + } + async function enableDisableSetting(resource: Uri, configTarget: ConfigurationTarget, setting: string, value: boolean) { + let settings = workspace.getConfiguration('python.linting', resource); + await settings.update(setting, value, configTarget); + settings = workspace.getConfiguration('python.linting', resource); + return settings.get(setting); + } + + async function testLinterSetting(resource: Uri, configTarget: ConfigurationTarget, setting: string, value: boolean) { + const valueInSetting = await enableDisableSetting(resource, configTarget, setting, value); + PythonSettings.dispose(); + const cfgSetting = PythonSettings.getInstance(resource); + assert.equal(valueInSetting, cfgSetting.linting[setting], `Both settings ${setting} should be ${value} for ${resource.fsPath}`); + } + + test('Workspace folder should inherit Python Path from workspace root', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + let settings = workspace.getConfiguration('python', workspaceUri); + const pythonPath = `x${new Date().getTime()}`; + await settings.update('pythonPath', pythonPath, ConfigurationTarget.Workspace); + + settings = workspace.getConfiguration('python', workspaceUri); + assert.equal(settings.get('pythonPath'), pythonPath, 'Python path not set in workspace root'); + + const cfgSetting = PythonSettings.getInstance(workspaceUri); + assert.equal(cfgSetting.pythonPath, pythonPath, 'Python Path not inherited from workspace'); + }); + + test('Workspace folder should not inherit Python Path from workspace root', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + let settings = workspace.getConfiguration('python', workspaceUri); + const pythonPath = `x${new Date().getTime()}`; + await settings.update('pythonPath', pythonPath, ConfigurationTarget.Workspace); + await settings.update('pythonPath', 'privatePythonPath', ConfigurationTarget.WorkspaceFolder); + + const cfgSetting = PythonSettings.getInstance(workspaceUri); + assert.equal(cfgSetting.pythonPath, 'privatePythonPath', 'Python Path for workspace folder is incorrect'); + }); + + + test('Workspace folder should inherit Python Path from workspace root when opening a document', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + const fileToOpen = path.join(multirootPath, 'workspace1', 'file.py'); + + const settings = workspace.getConfiguration('python', workspaceUri); + const pythonPath = `x${new Date().getTime()}`; + await settings.update('pythonPath', pythonPath, ConfigurationTarget.Workspace); + + const document = await workspace.openTextDocument(fileToOpen); + const cfg = PythonSettings.getInstance(document.uri); + assert.equal(cfg.pythonPath, pythonPath, 'Python Path not inherited from workspace'); + }); + + test('Workspace folder should not inherit Python Path from workspace root when opening a document', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + const fileToOpen = path.join(multirootPath, 'workspace1', 'file.py'); + + const settings = workspace.getConfiguration('python', workspaceUri); + const pythonPath = `x${new Date().getTime()}`; + await settings.update('pythonPath', pythonPath, ConfigurationTarget.Workspace); + await settings.update('pythonPath', 'privatePythonPath', ConfigurationTarget.WorkspaceFolder); + + const document = await workspace.openTextDocument(fileToOpen); + const cfg = PythonSettings.getInstance(document.uri); + assert.equal(cfg.pythonPath, 'privatePythonPath', 'Python Path for workspace folder is incorrect'); + }); + + test('Enabling/Disabling Pylint in root should be reflected in config settings', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', true); + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', false); + }); + + test('Enabling/Disabling Pylint in root should be reflected in config settings', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', true); + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', false); + }); + + test('Enabling/Disabling Pylint in root and workspace should be reflected in config settings', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + const workspaceFolder = Uri.file(path.join(multirootPath, 'workspace1')); + + await testLinterSetting(workspaceFolder, ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', false); + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', true); + + let cfgSetting = PythonSettings.getInstance(workspaceUri); + assert.equal(cfgSetting.linting.pylintEnabled, false, 'Workspace folder pylint setting is true when it should not be'); + PythonSettings.dispose(); + + await testLinterSetting(workspaceFolder, ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', false); + + cfgSetting = PythonSettings.getInstance(workspaceUri); + assert.equal(cfgSetting.linting.pylintEnabled, true, 'Workspace folder pylint setting is false when it should not be'); + }); + + test('Enabling/Disabling Pylint in root should be reflected in config settings when opening a document', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + const fileToOpen = path.join(multirootPath, 'workspace1', 'file.py'); + + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', false); + await testLinterSetting(workspaceUri, ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); + let document = await workspace.openTextDocument(fileToOpen); + let cfg = PythonSettings.getInstance(document.uri); + assert.equal(cfg.linting.pylintEnabled, true, 'Pylint should be enabled in workspace'); + PythonSettings.dispose(); + + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', true); + await testLinterSetting(workspaceUri, ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', false); + document = await workspace.openTextDocument(fileToOpen); + cfg = PythonSettings.getInstance(document.uri); + assert.equal(cfg.linting.pylintEnabled, false, 'Pylint should not be enabled in workspace'); + }); + + test('Enabling/Disabling Pylint in root should be reflected in config settings when opening a document', async () => { + const workspaceUri = Uri.file(path.join(multirootPath, 'workspace1')); + const fileToOpen = path.join(multirootPath, 'workspace1', 'file.py'); + + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', false); + await testLinterSetting(workspaceUri, ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); + let document = await workspace.openTextDocument(fileToOpen); + let cfg = PythonSettings.getInstance(document.uri); + assert.equal(cfg.linting.pylintEnabled, true, 'Pylint should be enabled in workspace'); + PythonSettings.dispose(); + + await testLinterSetting(workspaceUri, ConfigurationTarget.Workspace, 'pylintEnabled', true); + await testLinterSetting(workspaceUri, ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', false); + document = await workspace.openTextDocument(fileToOpen); + cfg = PythonSettings.getInstance(document.uri); + assert.equal(cfg.linting.pylintEnabled, false, 'Pylint should not be enabled in workspace'); + }); + + +}); diff --git a/src/test/index.ts b/src/test/index.ts index af916a981db9..af06fc19b486 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -1,4 +1,4 @@ -import { initializePython } from './initialize'; +import { initializePython, isMultitrootTest } from './initialize'; // // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING // @@ -11,14 +11,15 @@ import { initializePython } from './initialize'; // to report the results back to the caller. When the tests are finished, return // a possible error to the callback or null if none. -let testRunner = require('vscode/lib/testrunner'); - +const testRunner = require('vscode/lib/testrunner'); +const grep = isMultitrootTest() ? 'Multiroot' : undefined; // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results - timeout: 25000 + timeout: 25000, + grep }); initializePython(); diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 1dcd6e247fd4..0ed5ca288c13 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -89,3 +89,7 @@ export function initializePython() { const pythonConfig = vscode.workspace.getConfiguration('python'); pythonConfig.update('pythonPath', PYTHON_PATH); } + +export function isMultitrootTest() { + return Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0; +} diff --git a/src/test/linters/lint.multiroot.test.ts b/src/test/linters/lint.multiroot.test.ts new file mode 100644 index 000000000000..a53d468492a7 --- /dev/null +++ b/src/test/linters/lint.multiroot.test.ts @@ -0,0 +1,80 @@ +// import * as assert from 'assert'; +// import * as path from 'path'; +// import * as baseLinter from '../../client/linters/baseLinter'; +// import * as pyLint from '../../client/linters/pylint'; +// import * as flake8 from '../../client/linters/flake8'; +// import { PythonSettings } from '../../client/common/configSettings'; +// import { CancellationTokenSource, ConfigurationTarget, Uri, window, workspace } from 'vscode'; +// import { initialize, closeActiveWindows } from '../initialize'; +// import { MockOutputChannel } from '../mockClasses'; + +// const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); + +// suite('Multiroot Linting', () => { +// suiteSetup(async () => { +// await initialize(); +// PythonSettings.dispose(); +// }); +// suiteTeardown(() => closeActiveWindows()); +// teardown(async () => { +// await closeActiveWindows(); +// PythonSettings.dispose(); +// }); + +// async function testLinterInWorkspaceFolder(linter: baseLinter.BaseLinter, workspaceFolderRelativePath: string, mustHaveErrors: boolean) { +// const fileToLint = path.join(multirootPath, workspaceFolderRelativePath, 'file.py'); +// const cancelToken = new CancellationTokenSource(); +// const document = await workspace.openTextDocument(fileToLint); +// const editor = await window.showTextDocument(document); +// const messages = await linter.lint(editor.document, cancelToken.token); +// const errorMessage = mustHaveErrors ? 'No errors returned by linter' : 'Errors returned by linter'; +// assert.equal(messages.length > 0, mustHaveErrors, errorMessage); +// } +// async function enableDisableSetting(workspaceFolder, configTarget: ConfigurationTarget, setting: string, value: boolean) { +// const folderUri = Uri.file(workspaceFolder); +// const settings = workspace.getConfiguration('python.linting', folderUri); +// await settings.update(setting, value, configTarget); +// } + +// test('Enabling Pylint in root and also in Workspace, should return errors', async () => { +// let ch = new MockOutputChannel('Lint'); +// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', true); +// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); +// await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', true); +// }); + +// test('Enabling Pylint in root and disabling in Workspace, should not return errors', async () => { +// let ch = new MockOutputChannel('Lint'); +// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', true); +// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', false); +// await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', false); +// }); + +// test('Disabling Pylint in root and enabling in Workspace, should return errors', async () => { +// let ch = new MockOutputChannel('Lint'); +// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', false); +// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); +// await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', true); +// }); + +// test('Enabling Flake8 in root and also in Workspace, should return errors', async () => { +// let ch = new MockOutputChannel('Lint'); +// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', true); +// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', true); +// await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', true); +// }); + +// test('Enabling Flake8 in root and disabling in Workspace, should not return errors', async () => { +// let ch = new MockOutputChannel('Lint'); +// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', true); +// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', false); +// await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', false); +// }); + +// test('Disabling Flake8 in root and enabling in Workspace, should return errors', async () => { +// let ch = new MockOutputChannel('Lint'); +// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', false); +// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', true); +// await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', true); +// }); +// }); diff --git a/src/test/multiRootTest.ts b/src/test/multiRootTest.ts new file mode 100644 index 000000000000..b7ad307ef20c --- /dev/null +++ b/src/test/multiRootTest.ts @@ -0,0 +1,8 @@ +import * as path from 'path'; + +process.env.CODE_TESTS_WORKSPACE = path.join(__dirname, '..', '..', 'src', 'test', 'multiRootWkspc', 'multi.code-workspace'); + +function start() { + require('../../node_modules/vscode/bin/test'); +} +start(); diff --git a/src/test/multiRootWkspc/disableLinters/file.py b/src/test/multiRootWkspc/disableLinters/file.py new file mode 100644 index 000000000000..047ba0dc679e --- /dev/null +++ b/src/test/multiRootWkspc/disableLinters/file.py @@ -0,0 +1,87 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Foo(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1(self, arg): + """this issues a message""" + print self + + def meth2(self, arg): + """and this one not""" + # pylint: disable=unused-argument + print self\ + + "foo" + + def meth3(self): + """test one line disabling""" + # no error + print self.bla # pylint: disable=no-member + # error + print self.blop + + def meth4(self): + """test re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + print self.blop + # pylint: enable=no-member + # error + print self.blip + + def meth5(self): + """test IF sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + if self.blop: + # pylint: enable=no-member + # error + print self.blip + else: + # no error + print self.blip + # no error + print self.blip + + def meth6(self): + """test TRY/EXCEPT sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + try: + # pylint: enable=no-member + # error + print self.blip + except UndefinedName: # pylint: disable=undefined-variable + # no error + print self.blip + # no error + print self.blip + + def meth7(self): + """test one line block opening disabling""" + if self.blop: # pylint: disable=no-member + # error + print self.blip + else: + # error + print self.blip + # error + print self.blip + + + def meth8(self): + """test late disabling""" + # error + print self.blip + # pylint: disable=no-member + # no error + print self.bla + print self.blop \ No newline at end of file diff --git a/src/test/multiRootWkspc/multi.code-workspace b/src/test/multiRootWkspc/multi.code-workspace new file mode 100644 index 000000000000..bd948abca8a3 --- /dev/null +++ b/src/test/multiRootWkspc/multi.code-workspace @@ -0,0 +1,22 @@ +{ + "folders": [ + { + "path": "workspace1" + }, + { + "path": "parent\\child" + }, + { + "path": "disableLinters" + } + ], + "settings": { + "python.linting.flake8Enabled": false, + "python.linting.mypyEnabled": true, + "python.linting.pydocstyleEnabled": true, + "python.linting.pylamaEnabled": true, + "python.linting.pylintEnabled": true, + "python.linting.pep8Enabled": true, + "python.linting.prospectorEnabled": true + } +} \ No newline at end of file diff --git a/src/test/multiRootWkspc/parent/child/file.py b/src/test/multiRootWkspc/parent/child/file.py new file mode 100644 index 000000000000..047ba0dc679e --- /dev/null +++ b/src/test/multiRootWkspc/parent/child/file.py @@ -0,0 +1,87 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Foo(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1(self, arg): + """this issues a message""" + print self + + def meth2(self, arg): + """and this one not""" + # pylint: disable=unused-argument + print self\ + + "foo" + + def meth3(self): + """test one line disabling""" + # no error + print self.bla # pylint: disable=no-member + # error + print self.blop + + def meth4(self): + """test re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + print self.blop + # pylint: enable=no-member + # error + print self.blip + + def meth5(self): + """test IF sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + if self.blop: + # pylint: enable=no-member + # error + print self.blip + else: + # no error + print self.blip + # no error + print self.blip + + def meth6(self): + """test TRY/EXCEPT sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + try: + # pylint: enable=no-member + # error + print self.blip + except UndefinedName: # pylint: disable=undefined-variable + # no error + print self.blip + # no error + print self.blip + + def meth7(self): + """test one line block opening disabling""" + if self.blop: # pylint: disable=no-member + # error + print self.blip + else: + # error + print self.blip + # error + print self.blip + + + def meth8(self): + """test late disabling""" + # error + print self.blip + # pylint: disable=no-member + # no error + print self.bla + print self.blop \ No newline at end of file diff --git a/src/test/multiRootWkspc/workspace1/.vscode/settings.json b/src/test/multiRootWkspc/workspace1/.vscode/settings.json new file mode 100644 index 000000000000..6155e911580c --- /dev/null +++ b/src/test/multiRootWkspc/workspace1/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.linting.pylintEnabled": false, + "python.linting.enabled": false, + "python.linting.flake8Enabled": true +} \ No newline at end of file diff --git a/src/test/multiRootWkspc/workspace1/file.py b/src/test/multiRootWkspc/workspace1/file.py new file mode 100644 index 000000000000..047ba0dc679e --- /dev/null +++ b/src/test/multiRootWkspc/workspace1/file.py @@ -0,0 +1,87 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Foo(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1(self, arg): + """this issues a message""" + print self + + def meth2(self, arg): + """and this one not""" + # pylint: disable=unused-argument + print self\ + + "foo" + + def meth3(self): + """test one line disabling""" + # no error + print self.bla # pylint: disable=no-member + # error + print self.blop + + def meth4(self): + """test re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + print self.blop + # pylint: enable=no-member + # error + print self.blip + + def meth5(self): + """test IF sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + if self.blop: + # pylint: enable=no-member + # error + print self.blip + else: + # no error + print self.blip + # no error + print self.blip + + def meth6(self): + """test TRY/EXCEPT sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + try: + # pylint: enable=no-member + # error + print self.blip + except UndefinedName: # pylint: disable=undefined-variable + # no error + print self.blip + # no error + print self.blip + + def meth7(self): + """test one line block opening disabling""" + if self.blop: # pylint: disable=no-member + # error + print self.blip + else: + # error + print self.blip + # error + print self.blip + + + def meth8(self): + """test late disabling""" + # error + print self.blip + # pylint: disable=no-member + # no error + print self.bla + print self.blop \ No newline at end of file From b575dcc403e62d8cd4fe87f8da7eae23f6ad552d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 10:48:35 -0700 Subject: [PATCH 16/57] fix unittests for multiroot --- src/test/index.ts | 6 ++++-- src/test/initialize.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index af06fc19b486..e5fe465f9647 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -12,14 +12,16 @@ import { initializePython, isMultitrootTest } from './initialize'; // a possible error to the callback or null if none. const testRunner = require('vscode/lib/testrunner'); -const grep = isMultitrootTest() ? 'Multiroot' : undefined; +const invert = isMultitrootTest() ? undefined : 'invert'; + // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results timeout: 25000, - grep + grep : 'Multiroot', + invert }); initializePython(); diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 0ed5ca288c13..97b22674894f 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -91,5 +91,5 @@ export function initializePython() { } export function isMultitrootTest() { - return Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 0; + return Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 1; } From 7457c8920937d1ec0444c35bc629d8134cfb8ea9 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 10:49:26 -0700 Subject: [PATCH 17/57] exclude files --- .gitignore | 1 + .vscode/settings.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 58ce4158ddbe..f2183e471414 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ src/test/.vscode/** *.noseids .vscode-test __pycache__ +npm-debug.log \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index a09d3f11e14e..1b512d4d5c07 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ // Place your settings in this file to overwrite default and user settings. { "files.exclude": { - "out": false, // set this to true to hide the "out" folder with the compiled JS files + "out": true, // set this to true to hide the "out" folder with the compiled JS files "**/*.pyc": true, "**/__pycache__": true, "node_modules": true, From 54741c13d412ad722c6a454800a017e07090be67 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 14:25:21 -0700 Subject: [PATCH 18/57] add new line --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f2183e471414..cb3999278277 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ src/test/.vscode/** *.noseids .vscode-test __pycache__ -npm-debug.log \ No newline at end of file +npm-debug.log From ef306b001f9846747725671a08da95e46f8e69d3 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 5 Oct 2017 15:47:14 -0700 Subject: [PATCH 19/57] config changes for multiroot workspace From 23ab7518d50db78bac50cf99b516278a988125c7 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 14:59:12 -0700 Subject: [PATCH 20/57] new lines and enabled multi root linter tests --- src/test/linters/lint.multiroot.test.ts | 142 +++++++++--------- .../multiRootWkspc/disableLinters/file.py | 2 +- src/test/multiRootWkspc/multi.code-workspace | 4 +- src/test/multiRootWkspc/parent/child/file.py | 2 +- .../workspace1/.vscode/settings.json | 4 +- src/test/multiRootWkspc/workspace1/file.py | 2 +- 6 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/test/linters/lint.multiroot.test.ts b/src/test/linters/lint.multiroot.test.ts index a53d468492a7..e038ac9a1339 100644 --- a/src/test/linters/lint.multiroot.test.ts +++ b/src/test/linters/lint.multiroot.test.ts @@ -1,80 +1,80 @@ -// import * as assert from 'assert'; -// import * as path from 'path'; -// import * as baseLinter from '../../client/linters/baseLinter'; -// import * as pyLint from '../../client/linters/pylint'; -// import * as flake8 from '../../client/linters/flake8'; -// import { PythonSettings } from '../../client/common/configSettings'; -// import { CancellationTokenSource, ConfigurationTarget, Uri, window, workspace } from 'vscode'; -// import { initialize, closeActiveWindows } from '../initialize'; -// import { MockOutputChannel } from '../mockClasses'; +import * as assert from 'assert'; +import * as path from 'path'; +import * as baseLinter from '../../client/linters/baseLinter'; +import * as pyLint from '../../client/linters/pylint'; +import * as flake8 from '../../client/linters/flake8'; +import { PythonSettings } from '../../client/common/configSettings'; +import { CancellationTokenSource, ConfigurationTarget, Uri, window, workspace } from 'vscode'; +import { initialize, closeActiveWindows } from '../initialize'; +import { MockOutputChannel } from '../mockClasses'; -// const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); +const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); -// suite('Multiroot Linting', () => { -// suiteSetup(async () => { -// await initialize(); -// PythonSettings.dispose(); -// }); -// suiteTeardown(() => closeActiveWindows()); -// teardown(async () => { -// await closeActiveWindows(); -// PythonSettings.dispose(); -// }); +suite('Multiroot Linting', () => { + suiteSetup(async () => { + await initialize(); + PythonSettings.dispose(); + }); + suiteTeardown(() => closeActiveWindows()); + teardown(async () => { + await closeActiveWindows(); + PythonSettings.dispose(); + }); -// async function testLinterInWorkspaceFolder(linter: baseLinter.BaseLinter, workspaceFolderRelativePath: string, mustHaveErrors: boolean) { -// const fileToLint = path.join(multirootPath, workspaceFolderRelativePath, 'file.py'); -// const cancelToken = new CancellationTokenSource(); -// const document = await workspace.openTextDocument(fileToLint); -// const editor = await window.showTextDocument(document); -// const messages = await linter.lint(editor.document, cancelToken.token); -// const errorMessage = mustHaveErrors ? 'No errors returned by linter' : 'Errors returned by linter'; -// assert.equal(messages.length > 0, mustHaveErrors, errorMessage); -// } -// async function enableDisableSetting(workspaceFolder, configTarget: ConfigurationTarget, setting: string, value: boolean) { -// const folderUri = Uri.file(workspaceFolder); -// const settings = workspace.getConfiguration('python.linting', folderUri); -// await settings.update(setting, value, configTarget); -// } + async function testLinterInWorkspaceFolder(linter: baseLinter.BaseLinter, workspaceFolderRelativePath: string, mustHaveErrors: boolean) { + const fileToLint = path.join(multirootPath, workspaceFolderRelativePath, 'file.py'); + const cancelToken = new CancellationTokenSource(); + const document = await workspace.openTextDocument(fileToLint); + const editor = await window.showTextDocument(document); + const messages = await linter.lint(editor.document, cancelToken.token); + const errorMessage = mustHaveErrors ? 'No errors returned by linter' : 'Errors returned by linter'; + assert.equal(messages.length > 0, mustHaveErrors, errorMessage); + } + async function enableDisableSetting(workspaceFolder, configTarget: ConfigurationTarget, setting: string, value: boolean) { + const folderUri = Uri.file(workspaceFolder); + const settings = workspace.getConfiguration('python.linting', folderUri); + await settings.update(setting, value, configTarget); + } -// test('Enabling Pylint in root and also in Workspace, should return errors', async () => { -// let ch = new MockOutputChannel('Lint'); -// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', true); -// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); -// await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', true); -// }); + test('Enabling Pylint in root and also in Workspace, should return errors', async () => { + let ch = new MockOutputChannel('Lint'); + await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', true); + await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); + await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', true); + }); -// test('Enabling Pylint in root and disabling in Workspace, should not return errors', async () => { -// let ch = new MockOutputChannel('Lint'); -// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', true); -// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', false); -// await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', false); -// }); + test('Enabling Pylint in root and disabling in Workspace, should not return errors', async () => { + let ch = new MockOutputChannel('Lint'); + await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', true); + await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', false); + await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', false); + }); -// test('Disabling Pylint in root and enabling in Workspace, should return errors', async () => { -// let ch = new MockOutputChannel('Lint'); -// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', false); -// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); -// await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', true); -// }); + test('Disabling Pylint in root and enabling in Workspace, should return errors', async () => { + let ch = new MockOutputChannel('Lint'); + await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'pylintEnabled', false); + await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'pylintEnabled', true); + await testLinterInWorkspaceFolder(new pyLint.Linter(ch), 'workspace1', true); + }); -// test('Enabling Flake8 in root and also in Workspace, should return errors', async () => { -// let ch = new MockOutputChannel('Lint'); -// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', true); -// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', true); -// await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', true); -// }); + test('Enabling Flake8 in root and also in Workspace, should return errors', async () => { + let ch = new MockOutputChannel('Lint'); + await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', true); + await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', true); + await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', true); + }); -// test('Enabling Flake8 in root and disabling in Workspace, should not return errors', async () => { -// let ch = new MockOutputChannel('Lint'); -// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', true); -// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', false); -// await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', false); -// }); + test('Enabling Flake8 in root and disabling in Workspace, should not return errors', async () => { + let ch = new MockOutputChannel('Lint'); + await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', true); + await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', false); + await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', false); + }); -// test('Disabling Flake8 in root and enabling in Workspace, should return errors', async () => { -// let ch = new MockOutputChannel('Lint'); -// await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', false); -// await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', true); -// await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', true); -// }); -// }); + test('Disabling Flake8 in root and enabling in Workspace, should return errors', async () => { + let ch = new MockOutputChannel('Lint'); + await enableDisableSetting(multirootPath, ConfigurationTarget.Workspace, 'flake8Enabled', false); + await enableDisableSetting(path.join(multirootPath, 'workspace1'), ConfigurationTarget.WorkspaceFolder, 'flake8Enabled', true); + await testLinterInWorkspaceFolder(new flake8.Linter(ch), 'workspace1', true); + }); +}); diff --git a/src/test/multiRootWkspc/disableLinters/file.py b/src/test/multiRootWkspc/disableLinters/file.py index 047ba0dc679e..439f899e9e22 100644 --- a/src/test/multiRootWkspc/disableLinters/file.py +++ b/src/test/multiRootWkspc/disableLinters/file.py @@ -84,4 +84,4 @@ def meth8(self): # pylint: disable=no-member # no error print self.bla - print self.blop \ No newline at end of file + print self.blop diff --git a/src/test/multiRootWkspc/multi.code-workspace b/src/test/multiRootWkspc/multi.code-workspace index bd948abca8a3..bdb471d241b5 100644 --- a/src/test/multiRootWkspc/multi.code-workspace +++ b/src/test/multiRootWkspc/multi.code-workspace @@ -15,8 +15,8 @@ "python.linting.mypyEnabled": true, "python.linting.pydocstyleEnabled": true, "python.linting.pylamaEnabled": true, - "python.linting.pylintEnabled": true, + "python.linting.pylintEnabled": false, "python.linting.pep8Enabled": true, "python.linting.prospectorEnabled": true } -} \ No newline at end of file +} diff --git a/src/test/multiRootWkspc/parent/child/file.py b/src/test/multiRootWkspc/parent/child/file.py index 047ba0dc679e..439f899e9e22 100644 --- a/src/test/multiRootWkspc/parent/child/file.py +++ b/src/test/multiRootWkspc/parent/child/file.py @@ -84,4 +84,4 @@ def meth8(self): # pylint: disable=no-member # no error print self.bla - print self.blop \ No newline at end of file + print self.blop diff --git a/src/test/multiRootWkspc/workspace1/.vscode/settings.json b/src/test/multiRootWkspc/workspace1/.vscode/settings.json index 6155e911580c..a783cfe01962 100644 --- a/src/test/multiRootWkspc/workspace1/.vscode/settings.json +++ b/src/test/multiRootWkspc/workspace1/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.linting.pylintEnabled": false, + "python.linting.pylintEnabled": true, "python.linting.enabled": false, "python.linting.flake8Enabled": true -} \ No newline at end of file +} diff --git a/src/test/multiRootWkspc/workspace1/file.py b/src/test/multiRootWkspc/workspace1/file.py index 047ba0dc679e..439f899e9e22 100644 --- a/src/test/multiRootWkspc/workspace1/file.py +++ b/src/test/multiRootWkspc/workspace1/file.py @@ -84,4 +84,4 @@ def meth8(self): # pylint: disable=no-member # no error print self.bla - print self.blop \ No newline at end of file + print self.blop From b4295efde2522fd4459b209fdafee6648ff1ef69 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 17:09:43 -0700 Subject: [PATCH 21/57] fix sys variables --- src/client/common/systemVariables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/common/systemVariables.ts b/src/client/common/systemVariables.ts index e4adcaf67862..444d782d6eea 100644 --- a/src/client/common/systemVariables.ts +++ b/src/client/common/systemVariables.ts @@ -136,7 +136,7 @@ export class SystemVariables extends AbstractSystemVariables { constructor(workspaceRoot?: string) { super(); - this._workspaceRoot = typeof workspaceRoot === 'string' ? vscode.workspace.rootPath : __dirname; + this._workspaceRoot = typeof workspaceRoot === 'string' ? workspaceRoot : __dirname; this._workspaceRootFolderName = Path.basename(this._workspaceRoot); Object.keys(process.env).forEach(key => { this[`env:${key}`] = this[`env.${key}`] = process.env[key]; From 360e9198233a57784aee43cf306264ea08464d26 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 17:19:33 -0700 Subject: [PATCH 22/57] added unit test to resolve ${workspaceRoot} in settings.json --- .gitignore | 1 + .../common/configSettings.multiroot.test.ts | 16 ++++ src/test/multiRootWkspc/multi.code-workspace | 6 ++ .../workspace2/.vscode/settings.json | 3 + src/test/multiRootWkspc/workspace2/file.py | 87 +++++++++++++++++++ .../workspace3/.vscode/settings.json | 3 + src/test/multiRootWkspc/workspace3/file.py | 87 +++++++++++++++++++ 7 files changed, 203 insertions(+) create mode 100644 src/test/multiRootWkspc/workspace2/.vscode/settings.json create mode 100644 src/test/multiRootWkspc/workspace2/file.py create mode 100644 src/test/multiRootWkspc/workspace3/.vscode/settings.json create mode 100644 src/test/multiRootWkspc/workspace3/file.py diff --git a/.gitignore b/.gitignore index cb3999278277..6c4d63cbb81a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules *.pyc .vscode/.ropeproject/** src/test/.vscode/** +**/.vscode/tags **/testFiles/**/.cache/** *.noseids .vscode-test diff --git a/src/test/common/configSettings.multiroot.test.ts b/src/test/common/configSettings.multiroot.test.ts index b40b92ec6308..c7852babdb00 100644 --- a/src/test/common/configSettings.multiroot.test.ts +++ b/src/test/common/configSettings.multiroot.test.ts @@ -161,5 +161,21 @@ suite('Multiroot Config Settings', () => { assert.equal(cfg.linting.pylintEnabled, false, 'Pylint should not be enabled in workspace'); }); + test('${workspaceRoot} variable in settings should be replaced with the right value', async () => { + const workspace2Uri = Uri.file(path.join(multirootPath, 'workspace2')); + let fileToOpen = path.join(workspace2Uri.fsPath, 'file.py'); + let document = await workspace.openTextDocument(fileToOpen); + let cfg = PythonSettings.getInstance(document.uri); + assert.equal(path.dirname(cfg.workspaceSymbols.ctagsPath), workspace2Uri.fsPath, 'ctags file for workspace2 is incorrect'); + PythonSettings.dispose(); + + const workspace3Uri = Uri.file(path.join(multirootPath, 'workspace2')); + fileToOpen = path.join(workspace3Uri.fsPath, 'file.py'); + + document = await workspace.openTextDocument(fileToOpen); + cfg = PythonSettings.getInstance(document.uri); + assert.equal(path.dirname(cfg.workspaceSymbols.ctagsPath), workspace3Uri.fsPath, 'ctags file for workspace3 is incorrect'); + PythonSettings.dispose(); + }); }); diff --git a/src/test/multiRootWkspc/multi.code-workspace b/src/test/multiRootWkspc/multi.code-workspace index bdb471d241b5..67ea6996216d 100644 --- a/src/test/multiRootWkspc/multi.code-workspace +++ b/src/test/multiRootWkspc/multi.code-workspace @@ -3,6 +3,12 @@ { "path": "workspace1" }, + { + "path": "workspace2" + }, + { + "path": "workspace3" + }, { "path": "parent\\child" }, diff --git a/src/test/multiRootWkspc/workspace2/.vscode/settings.json b/src/test/multiRootWkspc/workspace2/.vscode/settings.json new file mode 100644 index 000000000000..1398df90f45b --- /dev/null +++ b/src/test/multiRootWkspc/workspace2/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.workspaceSymbols.ctagsPath": "${workspaceRoot}/workspace2.tags.file" +} diff --git a/src/test/multiRootWkspc/workspace2/file.py b/src/test/multiRootWkspc/workspace2/file.py new file mode 100644 index 000000000000..439f899e9e22 --- /dev/null +++ b/src/test/multiRootWkspc/workspace2/file.py @@ -0,0 +1,87 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Foo(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1(self, arg): + """this issues a message""" + print self + + def meth2(self, arg): + """and this one not""" + # pylint: disable=unused-argument + print self\ + + "foo" + + def meth3(self): + """test one line disabling""" + # no error + print self.bla # pylint: disable=no-member + # error + print self.blop + + def meth4(self): + """test re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + print self.blop + # pylint: enable=no-member + # error + print self.blip + + def meth5(self): + """test IF sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + if self.blop: + # pylint: enable=no-member + # error + print self.blip + else: + # no error + print self.blip + # no error + print self.blip + + def meth6(self): + """test TRY/EXCEPT sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + try: + # pylint: enable=no-member + # error + print self.blip + except UndefinedName: # pylint: disable=undefined-variable + # no error + print self.blip + # no error + print self.blip + + def meth7(self): + """test one line block opening disabling""" + if self.blop: # pylint: disable=no-member + # error + print self.blip + else: + # error + print self.blip + # error + print self.blip + + + def meth8(self): + """test late disabling""" + # error + print self.blip + # pylint: disable=no-member + # no error + print self.bla + print self.blop diff --git a/src/test/multiRootWkspc/workspace3/.vscode/settings.json b/src/test/multiRootWkspc/workspace3/.vscode/settings.json new file mode 100644 index 000000000000..1398df90f45b --- /dev/null +++ b/src/test/multiRootWkspc/workspace3/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.workspaceSymbols.ctagsPath": "${workspaceRoot}/workspace2.tags.file" +} diff --git a/src/test/multiRootWkspc/workspace3/file.py b/src/test/multiRootWkspc/workspace3/file.py new file mode 100644 index 000000000000..439f899e9e22 --- /dev/null +++ b/src/test/multiRootWkspc/workspace3/file.py @@ -0,0 +1,87 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Foo(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1(self, arg): + """this issues a message""" + print self + + def meth2(self, arg): + """and this one not""" + # pylint: disable=unused-argument + print self\ + + "foo" + + def meth3(self): + """test one line disabling""" + # no error + print self.bla # pylint: disable=no-member + # error + print self.blop + + def meth4(self): + """test re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + print self.blop + # pylint: enable=no-member + # error + print self.blip + + def meth5(self): + """test IF sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + if self.blop: + # pylint: enable=no-member + # error + print self.blip + else: + # no error + print self.blip + # no error + print self.blip + + def meth6(self): + """test TRY/EXCEPT sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + try: + # pylint: enable=no-member + # error + print self.blip + except UndefinedName: # pylint: disable=undefined-variable + # no error + print self.blip + # no error + print self.blip + + def meth7(self): + """test one line block opening disabling""" + if self.blop: # pylint: disable=no-member + # error + print self.blip + else: + # error + print self.blip + # error + print self.blip + + + def meth8(self): + """test late disabling""" + # error + print self.blip + # pylint: disable=no-member + # no error + print self.bla + print self.blop From 52e93c57a119171d592e7ad2fd63745e56c3d6d5 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 17:26:36 -0700 Subject: [PATCH 23/57] #1228 workspace symbols with multiroot support --- src/client/workspaceSymbols/generator.ts | 35 ++++++----- src/client/workspaceSymbols/main.ts | 75 +++++++++++++++--------- src/client/workspaceSymbols/parser.ts | 15 ++--- src/client/workspaceSymbols/provider.ts | 45 ++++++++------ 4 files changed, 102 insertions(+), 68 deletions(-) diff --git a/src/client/workspaceSymbols/generator.ts b/src/client/workspaceSymbols/generator.ts index 6f75a67cfed5..9833e3971389 100644 --- a/src/client/workspaceSymbols/generator.ts +++ b/src/client/workspaceSymbols/generator.ts @@ -2,17 +2,19 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import * as child_process from 'child_process'; -import { PythonSettings } from '../common/configSettings'; - -const pythonSettings = PythonSettings.getInstance(); +import { IPythonSettings, PythonSettings } from '../common/configSettings'; export class Generator implements vscode.Disposable { private optionsFile: string; private disposables: vscode.Disposable[]; - - constructor(private output: vscode.OutputChannel) { + private pythonSettings: IPythonSettings; + public get tagFilePath(): string { + return this.pythonSettings.workspaceSymbols.tagFilePath; + } + constructor(public readonly workspaceFolder: vscode.Uri, private output: vscode.OutputChannel) { this.disposables = []; this.optionsFile = path.join(__dirname, '..', '..', '..', 'resources', 'ctagOptions'); + this.pythonSettings = PythonSettings.getInstance(workspaceFolder); } dispose() { @@ -21,20 +23,25 @@ export class Generator implements vscode.Disposable { private buildCmdArgs(): string[] { const optionsFile = this.optionsFile.indexOf(' ') > 0 ? `"${this.optionsFile}"` : this.optionsFile; - const exclusions = pythonSettings.workspaceSymbols.exclusionPatterns; + const exclusions = this.pythonSettings.workspaceSymbols.exclusionPatterns; const excludes = exclusions.length === 0 ? [] : exclusions.map(pattern => `--exclude=${pattern}`); return [`--options=${optionsFile}`, '--languages=Python'].concat(excludes); } - generateWorkspaceTags(): Promise { - const tagFile = path.normalize(pythonSettings.workspaceSymbols.tagFilePath); - return this.generateTags(tagFile, { directory: vscode.workspace.rootPath }); + async generateWorkspaceTags(): Promise { + if (!this.pythonSettings.workspaceSymbols.enabled) { + return; + } + return await this.generateTags({ directory: this.workspaceFolder.fsPath }); } - private generateTags(outputFile: string, source: { directory?: string, file?: string }): Promise { - const cmd = pythonSettings.workspaceSymbols.ctagsPath; + private generateTags(source: { directory?: string, file?: string }): Promise { + const tagFile = path.normalize(this.pythonSettings.workspaceSymbols.tagFilePath); + const cmd = this.pythonSettings.workspaceSymbols.ctagsPath; const args = this.buildCmdArgs(); + + let outputFile = tagFile; if (source.file && source.file.length > 0) { source.directory = path.dirname(source.file); } @@ -43,14 +50,14 @@ export class Generator implements vscode.Disposable { outputFile = path.basename(outputFile); } const outputDir = path.dirname(outputFile); - if (!fs.existsSync(outputDir)){ + if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir); } outputFile = outputFile.indexOf(' ') > 0 ? `"${outputFile}"` : outputFile; args.push(`-o ${outputFile}`, '.'); this.output.appendLine('-'.repeat(10) + 'Generating Tags' + '-'.repeat(10)); this.output.appendLine(`${cmd} ${args.join(' ')}`); - const promise = new Promise((resolve, reject) => { + const promise = new Promise((resolve, reject) => { let options: child_process.SpawnOptions = { cwd: source.directory }; @@ -75,7 +82,7 @@ export class Generator implements vscode.Disposable { reject(errorMsg); } else { - resolve(outputFile); + resolve(); } }); }); diff --git a/src/client/workspaceSymbols/main.ts b/src/client/workspaceSymbols/main.ts index 5c0206290d0e..41ef3610be13 100644 --- a/src/client/workspaceSymbols/main.ts +++ b/src/client/workspaceSymbols/main.ts @@ -8,23 +8,34 @@ import { PythonLanguage, Commands } from '../common/constants'; import { WorkspaceSymbolProvider } from './provider'; const pythonSettings = PythonSettings.getInstance(); +const MAX_NUMBER_OF_ATTEMPTS_TO_INSTALL_AND_BUILD = 2; export class WorkspaceSymbols implements vscode.Disposable { private disposables: vscode.Disposable[]; - private generator: Generator; + private generators: Generator[] = []; private installer: Installer; constructor(private outputChannel: vscode.OutputChannel) { this.disposables = []; this.disposables.push(this.outputChannel); - this.generator = new Generator(this.outputChannel); - this.disposables.push(this.generator); this.installer = new Installer(); this.disposables.push(this.installer); this.registerCommands(); - - // The extension has just loaded, so lets rebuild the tags - vscode.languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.generator, this.outputChannel)); + this.initializeGenerators(); + vscode.languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.generators, this.outputChannel)); this.buildWorkspaceSymbols(true); + this.disposables.push(vscode.workspace.onDidChangeWorkspaceFolders(() => this.initializeGenerators())); + } + private initializeGenerators() { + while (this.generators.length > 0) { + const generator = this.generators.shift(); + generator.dispose(); + } + + if (Array.isArray(vscode.workspace.workspaceFolders)) { + vscode.workspace.workspaceFolders.forEach(wkSpc => { + this.generators.push(new Generator(wkSpc.uri, this.outputChannel)); + }); + } } registerCommands() { this.disposables.push(vscode.commands.registerCommand(Commands.Build_Workspace_Symbols, (rebuild: boolean = true, token?: vscode.CancellationToken) => { @@ -52,40 +63,50 @@ export class WorkspaceSymbols implements vscode.Disposable { dispose() { this.disposables.forEach(d => d.dispose()); } - buildWorkspaceSymbols(rebuild: boolean = true, token?: vscode.CancellationToken): Promise { + async buildWorkspaceSymbols(rebuild: boolean = true, token?: vscode.CancellationToken): Promise { if (!pythonSettings.workspaceSymbols.enabled || (token && token.isCancellationRequested)) { return Promise.resolve([]); } - if (!vscode.workspace || typeof vscode.workspace.rootPath !== 'string' || vscode.workspace.rootPath.length === 0) { + if (this.generators.length === 0) { return Promise.resolve([]); } - return fsExistsAsync(pythonSettings.workspaceSymbols.tagFilePath).then(exits => { - let promise = Promise.resolve(); + let promptPromise: Promise; + let promptResponse: InstallerResponse; + this.generators.map(async generator => { + const exists = await fsExistsAsync(generator.tagFilePath); // if file doesn't exist, then run the ctag generator // Or check if required to rebuild - if (rebuild || !exits) { - promise = this.generator.generateWorkspaceTags(); + if (!rebuild && exists) { + return; } - - return promise.catch(reason => { - if (!isNotInstalledError(reason)) { - this.outputChannel.show(); - return Promise.reject(reason); + for (let counter = 0; counter < MAX_NUMBER_OF_ATTEMPTS_TO_INSTALL_AND_BUILD; counter++) { + try { + await generator.generateWorkspaceTags(); + return; + } + catch (error) { + if (!isNotInstalledError(error)) { + this.outputChannel.show(); + return; + } } if (!token || token.isCancellationRequested) { return; } - return this.installer.promptToInstall(Product.ctags) - .then(result => { - if (!token || token.isCancellationRequested) { - return; - } - if (result === InstallerResponse.Installed) { - return this.buildWorkspaceSymbols(rebuild, token); - } - }); - }); + // Display prompt once for all workspaces + if (promptPromise) { + promptResponse = await promptPromise; + continue; + } + else { + promptPromise = this.installer.promptToInstall(Product.ctags); + promptResponse = await promptPromise; + } + if (promptResponse !== InstallerResponse.Installed || (!token || token.isCancellationRequested)) { + return; + } + } }); } } diff --git a/src/client/workspaceSymbols/parser.ts b/src/client/workspaceSymbols/parser.ts index e13d340434ef..7be6e51183d4 100644 --- a/src/client/workspaceSymbols/parser.ts +++ b/src/client/workspaceSymbols/parser.ts @@ -1,14 +1,12 @@ import * as vscode from 'vscode'; import { Tag } from './contracts'; import * as path from 'path'; -import { PythonSettings } from '../common/configSettings'; import { fsExistsAsync } from '../common/utils'; const LineByLineReader = require("line-by-line"); const NamedRegexp = require('named-js-regexp'); const fuzzy = require('fuzzy'); -const pythonSettings = PythonSettings.getInstance(); const IsFileRegEx = /\tkind:file\tline:\d+$/g; const LINE_REGEX = '(?\\w+)\\t(?.*)\\t\\/\\^(?.*)\\$\\/;"\\tkind:(?\\w+)\\tline:(?\\d+)$'; @@ -102,15 +100,14 @@ Object.keys(newValuesAndKeys).forEach(key => { CTagKinMapping.set(key, newValuesAndKeys[key]); }); -export function parseTags(query: string, token: vscode.CancellationToken, maxItems: number = 200): Promise { - const file = pythonSettings.workspaceSymbols.tagFilePath; - return fsExistsAsync(file).then(exists => { +export function parseTags(workspaceFolder: string, tagFile: string, query: string, token: vscode.CancellationToken, maxItems: number = 200): Promise { + return fsExistsAsync(tagFile).then(exists => { if (!exists) { return null; } return new Promise((resolve, reject) => { - let lr = new LineByLineReader(file); + let lr = new LineByLineReader(tagFile); let lineNumber = 0; let tags: Tag[] = []; @@ -124,7 +121,7 @@ export function parseTags(query: string, token: vscode.CancellationToken, maxIte lr.close(); return; } - const tag = parseTagsLine(line, query); + const tag = parseTagsLine(workspaceFolder, line, query); if (tag) { tags.push(tag); } @@ -139,7 +136,7 @@ export function parseTags(query: string, token: vscode.CancellationToken, maxIte }); }); } -function parseTagsLine(line: string, searchPattern: string): Tag { +function parseTagsLine(workspaceFolder: string, line: string, searchPattern: string): Tag { if (IsFileRegEx.test(line)) { return; } @@ -152,7 +149,7 @@ function parseTagsLine(line: string, searchPattern: string): Tag { } let file = match.file; if (!path.isAbsolute(file)) { - file = path.resolve(vscode.workspace.rootPath, '.vscode', file); + file = path.resolve(workspaceFolder, '.vscode', file); } const symbolKind = CTagKinMapping.has(match.type) ? CTagKinMapping.get(match.type) : vscode.SymbolKind.Null; diff --git a/src/client/workspaceSymbols/provider.ts b/src/client/workspaceSymbols/provider.ts index af02e06136c5..5a081fa19ef6 100644 --- a/src/client/workspaceSymbols/provider.ts +++ b/src/client/workspaceSymbols/provider.ts @@ -1,36 +1,45 @@ import * as vscode from 'vscode'; +import * as _ from 'lodash'; import { Generator } from './generator'; import { PythonSettings } from '../common/configSettings'; import { parseTags } from './parser'; import { fsExistsAsync } from '../common/utils'; -import { createDeferred } from '../common/helpers'; import { Commands } from '../common/constants'; const pythonSettings = PythonSettings.getInstance(); export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { - public constructor(private tagGenerator: Generator, private outputChannel: vscode.OutputChannel) { + public constructor(private tagGenerators: Generator[], private outputChannel: vscode.OutputChannel) { } async provideWorkspaceSymbols(query: string, token: vscode.CancellationToken): Promise { - if (!pythonSettings.workspaceSymbols.enabled) { + if (!pythonSettings.workspaceSymbols.enabled || this.tagGenerators.length === 0) { return []; } - if (!vscode.workspace || typeof vscode.workspace.rootPath !== 'string' || vscode.workspace.rootPath.length === 0) { - return Promise.resolve([]); - } - // check whether tag file needs to be built - const tagFileExists = await fsExistsAsync(pythonSettings.workspaceSymbols.tagFilePath); - if (!tagFileExists) { + const generatorsWithTagFiles = await Promise.all(this.tagGenerators.map(generator => fsExistsAsync(generator.tagFilePath))); + if (generatorsWithTagFiles.filter(exists => exists).length !== this.tagGenerators.length) { await vscode.commands.executeCommand(Commands.Build_Workspace_Symbols, true, token); } - // load tags - const items = await parseTags(query, token); - if (!Array.isArray(items)) { - return []; - } - return items.map(item => new vscode.SymbolInformation( - item.symbolName, item.symbolKind, '', - new vscode.Location(vscode.Uri.file(item.fileName), item.position) - )); + + const generators = await Promise.all(this.tagGenerators.map(async generator => { + const tagFileExists = await fsExistsAsync(generator.tagFilePath); + return tagFileExists ? generator : undefined; + })); + + const promises = generators + .filter(generator => generator !== undefined) + .map(async generator => { + // load tags + const items = await parseTags(generator.workspaceFolder.fsPath, generator.tagFilePath, query, token); + if (!Array.isArray(items)) { + return []; + } + return items.map(item => new vscode.SymbolInformation( + item.symbolName, item.symbolKind, '', + new vscode.Location(vscode.Uri.file(item.fileName), item.position) + )); + }); + + const symbols = await Promise.all(promises); + return _.flatten(symbols); } } From c2ba66530a01607bf14ca83e3da86843058911da Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 17:41:37 -0700 Subject: [PATCH 24/57] fix test --- src/test/common/configSettings.multiroot.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/common/configSettings.multiroot.test.ts b/src/test/common/configSettings.multiroot.test.ts index c7852babdb00..3c81186a9642 100644 --- a/src/test/common/configSettings.multiroot.test.ts +++ b/src/test/common/configSettings.multiroot.test.ts @@ -167,15 +167,17 @@ suite('Multiroot Config Settings', () => { let document = await workspace.openTextDocument(fileToOpen); let cfg = PythonSettings.getInstance(document.uri); - assert.equal(path.dirname(cfg.workspaceSymbols.ctagsPath), workspace2Uri.fsPath, 'ctags file for workspace2 is incorrect'); + assert.equal(path.dirname(cfg.workspaceSymbols.tagFilePath), workspace2Uri.fsPath, 'ctags file path for workspace2 is incorrect'); + assert.equal(path.basename(cfg.workspaceSymbols.tagFilePath), 'workspace2.tags.file', 'ctags file name for workspace2 is incorrect'); PythonSettings.dispose(); - const workspace3Uri = Uri.file(path.join(multirootPath, 'workspace2')); + const workspace3Uri = Uri.file(path.join(multirootPath, 'workspace3')); fileToOpen = path.join(workspace3Uri.fsPath, 'file.py'); document = await workspace.openTextDocument(fileToOpen); cfg = PythonSettings.getInstance(document.uri); - assert.equal(path.dirname(cfg.workspaceSymbols.ctagsPath), workspace3Uri.fsPath, 'ctags file for workspace3 is incorrect'); + assert.equal(path.dirname(cfg.workspaceSymbols.tagFilePath), workspace3Uri.fsPath, 'ctags file path for workspace3 is incorrect'); + assert.equal(path.basename(cfg.workspaceSymbols.tagFilePath), 'workspace3.tags.file', 'ctags file name for workspace3 is incorrect'); PythonSettings.dispose(); }); }); From e0f57ce098afb41c67619880063f3b18677db441 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 11 Oct 2017 17:41:46 -0700 Subject: [PATCH 25/57] added some data for workspace symbol tests --- .gitignore | 1 - .../disableLinters/.vscode/tags | 19 +++++++++++++++++++ .../parent/child/.vscode/settings.json | 3 +++ .../multiRootWkspc/parent/child/.vscode/tags | 19 +++++++++++++++++++ .../multiRootWkspc/workspace1/.vscode/tags | 19 +++++++++++++++++++ .../workspace2/.vscode/settings.json | 2 +- .../workspace2/workspace2.tags.file | 19 +++++++++++++++++++ .../workspace3/.vscode/settings.json | 2 +- .../workspace3/workspace3.tags.file | 19 +++++++++++++++++++ 9 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 src/test/multiRootWkspc/disableLinters/.vscode/tags create mode 100644 src/test/multiRootWkspc/parent/child/.vscode/settings.json create mode 100644 src/test/multiRootWkspc/parent/child/.vscode/tags create mode 100644 src/test/multiRootWkspc/workspace1/.vscode/tags create mode 100644 src/test/multiRootWkspc/workspace2/workspace2.tags.file create mode 100644 src/test/multiRootWkspc/workspace3/workspace3.tags.file diff --git a/.gitignore b/.gitignore index 6c4d63cbb81a..cb3999278277 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ node_modules *.pyc .vscode/.ropeproject/** src/test/.vscode/** -**/.vscode/tags **/testFiles/**/.cache/** *.noseids .vscode-test diff --git a/src/test/multiRootWkspc/disableLinters/.vscode/tags b/src/test/multiRootWkspc/disableLinters/.vscode/tags new file mode 100644 index 000000000000..4739b4629cfb --- /dev/null +++ b/src/test/multiRootWkspc/disableLinters/.vscode/tags @@ -0,0 +1,19 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +Foo ..\\file.py /^class Foo(object):$/;" kind:class line:5 +__init__ ..\\file.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ ..\\file.py /^__revision__ = None$/;" kind:variable line:3 +file.py ..\\file.py 1;" kind:file line:1 +meth1 ..\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth2 ..\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 ..\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 ..\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 ..\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 ..\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 ..\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 ..\\file.py /^ def meth8(self):$/;" kind:member line:80 diff --git a/src/test/multiRootWkspc/parent/child/.vscode/settings.json b/src/test/multiRootWkspc/parent/child/.vscode/settings.json new file mode 100644 index 000000000000..c404e94945a9 --- /dev/null +++ b/src/test/multiRootWkspc/parent/child/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.workspaceSymbols.enabled": false +} \ No newline at end of file diff --git a/src/test/multiRootWkspc/parent/child/.vscode/tags b/src/test/multiRootWkspc/parent/child/.vscode/tags new file mode 100644 index 000000000000..4739b4629cfb --- /dev/null +++ b/src/test/multiRootWkspc/parent/child/.vscode/tags @@ -0,0 +1,19 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +Foo ..\\file.py /^class Foo(object):$/;" kind:class line:5 +__init__ ..\\file.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ ..\\file.py /^__revision__ = None$/;" kind:variable line:3 +file.py ..\\file.py 1;" kind:file line:1 +meth1 ..\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth2 ..\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 ..\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 ..\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 ..\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 ..\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 ..\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 ..\\file.py /^ def meth8(self):$/;" kind:member line:80 diff --git a/src/test/multiRootWkspc/workspace1/.vscode/tags b/src/test/multiRootWkspc/workspace1/.vscode/tags new file mode 100644 index 000000000000..4739b4629cfb --- /dev/null +++ b/src/test/multiRootWkspc/workspace1/.vscode/tags @@ -0,0 +1,19 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +Foo ..\\file.py /^class Foo(object):$/;" kind:class line:5 +__init__ ..\\file.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ ..\\file.py /^__revision__ = None$/;" kind:variable line:3 +file.py ..\\file.py 1;" kind:file line:1 +meth1 ..\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth2 ..\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 ..\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 ..\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 ..\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 ..\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 ..\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 ..\\file.py /^ def meth8(self):$/;" kind:member line:80 diff --git a/src/test/multiRootWkspc/workspace2/.vscode/settings.json b/src/test/multiRootWkspc/workspace2/.vscode/settings.json index 1398df90f45b..d33a10ac30d5 100644 --- a/src/test/multiRootWkspc/workspace2/.vscode/settings.json +++ b/src/test/multiRootWkspc/workspace2/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.workspaceSymbols.ctagsPath": "${workspaceRoot}/workspace2.tags.file" + "python.workspaceSymbols.tagFilePath": "${workspaceRoot}/workspace2.tags.file" } diff --git a/src/test/multiRootWkspc/workspace2/workspace2.tags.file b/src/test/multiRootWkspc/workspace2/workspace2.tags.file new file mode 100644 index 000000000000..31376a03b17c --- /dev/null +++ b/src/test/multiRootWkspc/workspace2/workspace2.tags.file @@ -0,0 +1,19 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +Foo C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^class Foo(object):$/;" kind:class line:5 +__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^__revision__ = None$/;" kind:variable line:3 +file.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py 1;" kind:file line:1 +meth1 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth8(self):$/;" kind:member line:80 diff --git a/src/test/multiRootWkspc/workspace3/.vscode/settings.json b/src/test/multiRootWkspc/workspace3/.vscode/settings.json index 1398df90f45b..8779a0c08efe 100644 --- a/src/test/multiRootWkspc/workspace3/.vscode/settings.json +++ b/src/test/multiRootWkspc/workspace3/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.workspaceSymbols.ctagsPath": "${workspaceRoot}/workspace2.tags.file" + "python.workspaceSymbols.tagFilePath": "${workspaceRoot}/workspace3.tags.file" } diff --git a/src/test/multiRootWkspc/workspace3/workspace3.tags.file b/src/test/multiRootWkspc/workspace3/workspace3.tags.file new file mode 100644 index 000000000000..9a141392d6ae --- /dev/null +++ b/src/test/multiRootWkspc/workspace3/workspace3.tags.file @@ -0,0 +1,19 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +Foo C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^class Foo(object):$/;" kind:class line:5 +__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^__revision__ = None$/;" kind:variable line:3 +file.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py 1;" kind:file line:1 +meth1 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth8(self):$/;" kind:member line:80 From 9e79d26ff6f4dcc619b580ba3279777da3215607 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 08:56:34 -0700 Subject: [PATCH 26/57] data for unit tests --- src/test/multiRootWkspc/multi.code-workspace | 3 +- .../multiRootWkspc/parent/child/.vscode/tags | 5 ++ .../multiRootWkspc/parent/child/childFile.py | 13 +++ .../workspace2/.vscode/settings.json | 3 +- .../workspace2/workspace2.tags.file | 5 ++ .../workspace2/workspace2File.py | 13 +++ src/test/pythonFiles/symbolFiles/childFile.py | 13 +++ src/test/pythonFiles/symbolFiles/file.py | 87 +++++++++++++++++++ .../pythonFiles/symbolFiles/workspace2File.py | 13 +++ 9 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/test/multiRootWkspc/parent/child/childFile.py create mode 100644 src/test/multiRootWkspc/workspace2/workspace2File.py create mode 100644 src/test/pythonFiles/symbolFiles/childFile.py create mode 100644 src/test/pythonFiles/symbolFiles/file.py create mode 100644 src/test/pythonFiles/symbolFiles/workspace2File.py diff --git a/src/test/multiRootWkspc/multi.code-workspace b/src/test/multiRootWkspc/multi.code-workspace index 67ea6996216d..3c2ef8e8ca44 100644 --- a/src/test/multiRootWkspc/multi.code-workspace +++ b/src/test/multiRootWkspc/multi.code-workspace @@ -23,6 +23,7 @@ "python.linting.pylamaEnabled": true, "python.linting.pylintEnabled": false, "python.linting.pep8Enabled": true, - "python.linting.prospectorEnabled": true + "python.linting.prospectorEnabled": true, + "python.workspaceSymbols.enabled": true } } diff --git a/src/test/multiRootWkspc/parent/child/.vscode/tags b/src/test/multiRootWkspc/parent/child/.vscode/tags index 4739b4629cfb..e6791c755b0f 100644 --- a/src/test/multiRootWkspc/parent/child/.vscode/tags +++ b/src/test/multiRootWkspc/parent/child/.vscode/tags @@ -5,11 +5,16 @@ !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ !_TAG_PROGRAM_URL https://ctags.io/ /official site/ !_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +Child2Class ..\\childFile.py /^class Child2Class(object):$/;" kind:class line:5 Foo ..\\file.py /^class Foo(object):$/;" kind:class line:5 +__init__ ..\\childFile.py /^ def __init__(self):$/;" kind:member line:8 __init__ ..\\file.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ ..\\childFile.py /^__revision__ = None$/;" kind:variable line:3 __revision__ ..\\file.py /^__revision__ = None$/;" kind:variable line:3 +childFile.py ..\\childFile.py 1;" kind:file line:1 file.py ..\\file.py 1;" kind:file line:1 meth1 ..\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1OfChild ..\\childFile.py /^ def meth1OfChild(self, arg):$/;" kind:member line:11 meth2 ..\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 meth3 ..\\file.py /^ def meth3(self):$/;" kind:member line:21 meth4 ..\\file.py /^ def meth4(self):$/;" kind:member line:28 diff --git a/src/test/multiRootWkspc/parent/child/childFile.py b/src/test/multiRootWkspc/parent/child/childFile.py new file mode 100644 index 000000000000..82547b176285 --- /dev/null +++ b/src/test/multiRootWkspc/parent/child/childFile.py @@ -0,0 +1,13 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Child2Class(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1OfChild(self, arg): + """this issues a message""" + print self diff --git a/src/test/multiRootWkspc/workspace2/.vscode/settings.json b/src/test/multiRootWkspc/workspace2/.vscode/settings.json index d33a10ac30d5..385728982cfa 100644 --- a/src/test/multiRootWkspc/workspace2/.vscode/settings.json +++ b/src/test/multiRootWkspc/workspace2/.vscode/settings.json @@ -1,3 +1,4 @@ { - "python.workspaceSymbols.tagFilePath": "${workspaceRoot}/workspace2.tags.file" + "python.workspaceSymbols.tagFilePath": "${workspaceRoot}/workspace2.tags.file", + "python.workspaceSymbols.enabled": true } diff --git a/src/test/multiRootWkspc/workspace2/workspace2.tags.file b/src/test/multiRootWkspc/workspace2/workspace2.tags.file index 31376a03b17c..2d54e7ed7c7b 100644 --- a/src/test/multiRootWkspc/workspace2/workspace2.tags.file +++ b/src/test/multiRootWkspc/workspace2/workspace2.tags.file @@ -6,10 +6,14 @@ !_TAG_PROGRAM_URL https://ctags.io/ /official site/ !_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ Foo C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^class Foo(object):$/;" kind:class line:5 +Workspace2Class C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^class Workspace2Class(object):$/;" kind:class line:5 __init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^ def __init__(self):$/;" kind:member line:8 __revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^__revision__ = None$/;" kind:variable line:3 file.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py 1;" kind:file line:1 meth1 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1OfWorkspace2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^ def meth1OfWorkspace2(self, arg):$/;" kind:member line:11 meth2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 meth3 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth3(self):$/;" kind:member line:21 meth4 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth4(self):$/;" kind:member line:28 @@ -17,3 +21,4 @@ meth5 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRo meth6 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth6(self):$/;" kind:member line:53 meth7 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth7(self):$/;" kind:member line:68 meth8 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth8(self):$/;" kind:member line:80 +workspace2File.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py 1;" kind:file line:1 diff --git a/src/test/multiRootWkspc/workspace2/workspace2File.py b/src/test/multiRootWkspc/workspace2/workspace2File.py new file mode 100644 index 000000000000..2958414e1e17 --- /dev/null +++ b/src/test/multiRootWkspc/workspace2/workspace2File.py @@ -0,0 +1,13 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Workspace2Class(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1OfWorkspace2(self, arg): + """this issues a message""" + print self diff --git a/src/test/pythonFiles/symbolFiles/childFile.py b/src/test/pythonFiles/symbolFiles/childFile.py new file mode 100644 index 000000000000..82547b176285 --- /dev/null +++ b/src/test/pythonFiles/symbolFiles/childFile.py @@ -0,0 +1,13 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Child2Class(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1OfChild(self, arg): + """this issues a message""" + print self diff --git a/src/test/pythonFiles/symbolFiles/file.py b/src/test/pythonFiles/symbolFiles/file.py new file mode 100644 index 000000000000..439f899e9e22 --- /dev/null +++ b/src/test/pythonFiles/symbolFiles/file.py @@ -0,0 +1,87 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Foo(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1(self, arg): + """this issues a message""" + print self + + def meth2(self, arg): + """and this one not""" + # pylint: disable=unused-argument + print self\ + + "foo" + + def meth3(self): + """test one line disabling""" + # no error + print self.bla # pylint: disable=no-member + # error + print self.blop + + def meth4(self): + """test re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + print self.blop + # pylint: enable=no-member + # error + print self.blip + + def meth5(self): + """test IF sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + if self.blop: + # pylint: enable=no-member + # error + print self.blip + else: + # no error + print self.blip + # no error + print self.blip + + def meth6(self): + """test TRY/EXCEPT sub-block re-enabling""" + # pylint: disable=no-member + # no error + print self.bla + try: + # pylint: enable=no-member + # error + print self.blip + except UndefinedName: # pylint: disable=undefined-variable + # no error + print self.blip + # no error + print self.blip + + def meth7(self): + """test one line block opening disabling""" + if self.blop: # pylint: disable=no-member + # error + print self.blip + else: + # error + print self.blip + # error + print self.blip + + + def meth8(self): + """test late disabling""" + # error + print self.blip + # pylint: disable=no-member + # no error + print self.bla + print self.blop diff --git a/src/test/pythonFiles/symbolFiles/workspace2File.py b/src/test/pythonFiles/symbolFiles/workspace2File.py new file mode 100644 index 000000000000..2958414e1e17 --- /dev/null +++ b/src/test/pythonFiles/symbolFiles/workspace2File.py @@ -0,0 +1,13 @@ +"""pylint option block-disable""" + +__revision__ = None + +class Workspace2Class(object): + """block-disable test""" + + def __init__(self): + pass + + def meth1OfWorkspace2(self, arg): + """this issues a message""" + print self From bbf2403afb4fa8d1ce4df515d018e3bc3fed9df7 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 08:56:53 -0700 Subject: [PATCH 27/57] fixed to add support for multit roots with unit tests --- src/client/workspaceSymbols/generator.ts | 3 + src/client/workspaceSymbols/main.ts | 9 +-- src/client/workspaceSymbols/provider.ts | 6 +- .../common/configSettings.multiroot.test.ts | 2 +- src/test/workspaceSymbols/common.ts | 8 +++ src/test/workspaceSymbols/multiroot.test.ts | 68 ++++++++++++++++++ src/test/workspaceSymbols/standard.test.ts | 69 +++++++++++++++++++ 7 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 src/test/workspaceSymbols/common.ts create mode 100644 src/test/workspaceSymbols/multiroot.test.ts create mode 100644 src/test/workspaceSymbols/standard.test.ts diff --git a/src/client/workspaceSymbols/generator.ts b/src/client/workspaceSymbols/generator.ts index 9833e3971389..dcb1b5ae3d35 100644 --- a/src/client/workspaceSymbols/generator.ts +++ b/src/client/workspaceSymbols/generator.ts @@ -11,6 +11,9 @@ export class Generator implements vscode.Disposable { public get tagFilePath(): string { return this.pythonSettings.workspaceSymbols.tagFilePath; } + public get enabled(): boolean { + return this.pythonSettings.workspaceSymbols.enabled; + } constructor(public readonly workspaceFolder: vscode.Uri, private output: vscode.OutputChannel) { this.disposables = []; this.optionsFile = path.join(__dirname, '..', '..', '..', 'resources', 'ctagOptions'); diff --git a/src/client/workspaceSymbols/main.ts b/src/client/workspaceSymbols/main.ts index 41ef3610be13..3a5c64e55b4e 100644 --- a/src/client/workspaceSymbols/main.ts +++ b/src/client/workspaceSymbols/main.ts @@ -1,13 +1,11 @@ import * as vscode from 'vscode'; import { Generator } from './generator'; import { Installer, InstallerResponse, Product } from '../common/installer'; -import { PythonSettings } from '../common/configSettings'; import { fsExistsAsync } from '../common/utils'; import { isNotInstalledError } from '../common/helpers'; import { PythonLanguage, Commands } from '../common/constants'; import { WorkspaceSymbolProvider } from './provider'; -const pythonSettings = PythonSettings.getInstance(); const MAX_NUMBER_OF_ATTEMPTS_TO_INSTALL_AND_BUILD = 2; export class WorkspaceSymbols implements vscode.Disposable { @@ -64,7 +62,7 @@ export class WorkspaceSymbols implements vscode.Disposable { this.disposables.forEach(d => d.dispose()); } async buildWorkspaceSymbols(rebuild: boolean = true, token?: vscode.CancellationToken): Promise { - if (!pythonSettings.workspaceSymbols.enabled || (token && token.isCancellationRequested)) { + if (token && token.isCancellationRequested) { return Promise.resolve([]); } if (this.generators.length === 0) { @@ -73,7 +71,10 @@ export class WorkspaceSymbols implements vscode.Disposable { let promptPromise: Promise; let promptResponse: InstallerResponse; - this.generators.map(async generator => { + return this.generators.map(async generator => { + if (!generator.enabled) { + return; + } const exists = await fsExistsAsync(generator.tagFilePath); // if file doesn't exist, then run the ctag generator // Or check if required to rebuild diff --git a/src/client/workspaceSymbols/provider.ts b/src/client/workspaceSymbols/provider.ts index 5a081fa19ef6..e9124212e9a8 100644 --- a/src/client/workspaceSymbols/provider.ts +++ b/src/client/workspaceSymbols/provider.ts @@ -1,18 +1,16 @@ import * as vscode from 'vscode'; import * as _ from 'lodash'; import { Generator } from './generator'; -import { PythonSettings } from '../common/configSettings'; import { parseTags } from './parser'; import { fsExistsAsync } from '../common/utils'; import { Commands } from '../common/constants'; -const pythonSettings = PythonSettings.getInstance(); export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { public constructor(private tagGenerators: Generator[], private outputChannel: vscode.OutputChannel) { } async provideWorkspaceSymbols(query: string, token: vscode.CancellationToken): Promise { - if (!pythonSettings.workspaceSymbols.enabled || this.tagGenerators.length === 0) { + if (this.tagGenerators.length === 0) { return []; } const generatorsWithTagFiles = await Promise.all(this.tagGenerators.map(generator => fsExistsAsync(generator.tagFilePath))); @@ -26,7 +24,7 @@ export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { })); const promises = generators - .filter(generator => generator !== undefined) + .filter(generator => generator !== undefined && generator.enabled) .map(async generator => { // load tags const items = await parseTags(generator.workspaceFolder.fsPath, generator.tagFilePath, query, token); diff --git a/src/test/common/configSettings.multiroot.test.ts b/src/test/common/configSettings.multiroot.test.ts index 3c81186a9642..4157903d28df 100644 --- a/src/test/common/configSettings.multiroot.test.ts +++ b/src/test/common/configSettings.multiroot.test.ts @@ -14,7 +14,7 @@ suite('Multiroot Config Settings', () => { suiteTeardown(() => closeActiveWindows()); teardown(async () => { await resetSettings(); - // await closeActiveWindows(); + await closeActiveWindows(); }); async function resetSettings() { diff --git a/src/test/workspaceSymbols/common.ts b/src/test/workspaceSymbols/common.ts new file mode 100644 index 000000000000..aa4338db5a0c --- /dev/null +++ b/src/test/workspaceSymbols/common.ts @@ -0,0 +1,8 @@ +import { ConfigurationTarget, Uri, workspace } from 'vscode'; +import { PythonSettings } from '../../client/common/configSettings'; + +export async function enableDisableWorkspaceSymbols(resource: Uri, enabled: boolean) { + let settings = workspace.getConfiguration('python', resource); + await settings.update('workspaceSymbols.enabled', enabled, ConfigurationTarget.WorkspaceFolder); + PythonSettings.dispose(); +} \ No newline at end of file diff --git a/src/test/workspaceSymbols/multiroot.test.ts b/src/test/workspaceSymbols/multiroot.test.ts new file mode 100644 index 000000000000..331ca5d7e337 --- /dev/null +++ b/src/test/workspaceSymbols/multiroot.test.ts @@ -0,0 +1,68 @@ +import * as assert from 'assert'; +import * as path from 'path'; +import { CancellationTokenSource, ConfigurationTarget, Uri, workspace } from 'vscode'; +import { initialize, closeActiveWindows } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { Generator } from '../../client/workspaceSymbols/generator'; +import { MockOutputChannel } from '../mockClasses'; +import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; +import { enableDisableWorkspaceSymbols } from './common'; + +const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); + +suite('Multiroot Workspace Symbols', () => { + suiteSetup(() => initialize()); + suiteTeardown(() => closeActiveWindows()); + teardown(async () => { + await closeActiveWindows(); + await resetSettings(); + }); + + async function resetSettings() { + PythonSettings.dispose(); + const childWorkspaceUri = Uri.file(path.join(multirootPath, 'parent', 'child')); + const settings = workspace.getConfiguration('python', childWorkspaceUri); + const value = settings.inspect('workspaceSymbols.enabled'); + if (value.workspaceFolderValue !== false) { + await settings.update('workspaceSymbols.enabled', false, ConfigurationTarget.WorkspaceFolder); + } + } + test(`symbols should be returned when enabeld and vice versa`, async () => { + const childWorkspaceUri = Uri.file(path.join(multirootPath, 'parent', 'child')); + const outputChannel = new MockOutputChannel('Output'); + + await enableDisableWorkspaceSymbols(childWorkspaceUri, false); + + let generator = new Generator(childWorkspaceUri, outputChannel); + let provider = new WorkspaceSymbolProvider([generator], outputChannel); + let symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); + assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); + generator.dispose(); + + await enableDisableWorkspaceSymbols(childWorkspaceUri, true); + + generator = new Generator(childWorkspaceUri, outputChannel); + provider = new WorkspaceSymbolProvider([generator], outputChannel); + symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); + assert.notEqual(symbols.length, 0, 'Symbols should be returned when workspace symbols are turned on'); + }); + test(`symbols should be filtered correctly`, async () => { + const childWorkspaceUri = Uri.file(path.join(multirootPath, 'parent', 'child')); + const workspace2Uri = Uri.file(path.join(multirootPath, 'workspace2')); + const outputChannel = new MockOutputChannel('Output'); + + await enableDisableWorkspaceSymbols(childWorkspaceUri, true); + await enableDisableWorkspaceSymbols(workspace2Uri, true); + + const generators = [ + new Generator(childWorkspaceUri, outputChannel), + new Generator(workspace2Uri, outputChannel)]; + const provider = new WorkspaceSymbolProvider(generators, outputChannel); + const symbols = await provider.provideWorkspaceSymbols('meth1Of', new CancellationTokenSource().token); + + // Remember (multiroot workspace is a child directory, hence we have more python files to account for in there) + assert.equal(symbols.length >= 2, true, 'Incorrect number of symbols returned'); + assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'File with symbol not found in child workspace folder'); + assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'File with symbol not found in child workspace folder'); + }); +}); diff --git a/src/test/workspaceSymbols/standard.test.ts b/src/test/workspaceSymbols/standard.test.ts new file mode 100644 index 000000000000..b8b313bffd93 --- /dev/null +++ b/src/test/workspaceSymbols/standard.test.ts @@ -0,0 +1,69 @@ +import * as assert from 'assert'; +import * as path from 'path'; +import { CancellationTokenSource, ConfigurationTarget, Uri, workspace } from 'vscode'; +import { initialize, closeActiveWindows } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { Generator } from '../../client/workspaceSymbols/generator'; +import { MockOutputChannel } from '../mockClasses'; +import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; +import { enableDisableWorkspaceSymbols } from './common'; + +const symbolFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'symbolFiles'); + +suite('Workspace Symbols', () => { + suiteSetup(() => initialize()); + suiteTeardown(() => closeActiveWindows()); + teardown(async () => { + await closeActiveWindows(); + await resetSettings(); + }); + + async function resetSettings() { + PythonSettings.dispose(); + const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); + const settings = workspace.getConfiguration('python', workspaceUri); + const value = settings.inspect('workspaceSymbols.enabled'); + if (value.workspaceFolderValue !== true) { + await settings.update('workspaceSymbols.enabled', true, ConfigurationTarget.Workspace); + } + } + test(`symbols should be returned when enabeld and vice versa`, async () => { + const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); + const outputChannel = new MockOutputChannel('Output'); + + await enableDisableWorkspaceSymbols(workspaceUri, false); + + let generator = new Generator(workspaceUri, outputChannel); + let provider = new WorkspaceSymbolProvider([generator], outputChannel); + let symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); + assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); + generator.dispose(); + + await enableDisableWorkspaceSymbols(workspaceUri, true); + + generator = new Generator(workspaceUri, outputChannel); + provider = new WorkspaceSymbolProvider([generator], outputChannel); + symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); + assert.notEqual(symbols.length, 0, 'Symbols should be returned when workspace symbols are turned on'); + }); + test(`symbols should be filtered correctly`, async () => { + const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); + const outputChannel = new MockOutputChannel('Output'); + + await enableDisableWorkspaceSymbols(workspaceUri, true); + + const generators = [new Generator(workspaceUri, outputChannel)]; + const provider = new WorkspaceSymbolProvider(generators, outputChannel); + const symbols = await provider.provideWorkspaceSymbols('meth1Of', new CancellationTokenSource().token); + + assert.equal(symbols.length, 2, 'Incorrect number of symbols returned'); + assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'File with symbol not found in child workspace folder'); + assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'File with symbol not found in child workspace folder'); + + const symbolsForMeth = await provider.provideWorkspaceSymbols('meth', new CancellationTokenSource().token); + assert.equal(symbolsForMeth.length, 10, 'Incorrect number of symbols returned'); + assert.notEqual(symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'Symbols not returned for childFile.py'); + assert.notEqual(symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'Symbols not returned for workspace2File.py'); + assert.notEqual(symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('file.py')), -1, 'Symbols not returned for file.py'); + }); +}); From 57ce355774919804083b7433ef9aef0b89e62237 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 10:38:01 -0700 Subject: [PATCH 28/57] account for mutiroot files in sub directory --- src/test/workspaceSymbols/standard.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/workspaceSymbols/standard.test.ts b/src/test/workspaceSymbols/standard.test.ts index b8b313bffd93..68cc5d00184e 100644 --- a/src/test/workspaceSymbols/standard.test.ts +++ b/src/test/workspaceSymbols/standard.test.ts @@ -56,12 +56,12 @@ suite('Workspace Symbols', () => { const provider = new WorkspaceSymbolProvider(generators, outputChannel); const symbols = await provider.provideWorkspaceSymbols('meth1Of', new CancellationTokenSource().token); - assert.equal(symbols.length, 2, 'Incorrect number of symbols returned'); + assert.equal(symbols.length >= 2, true, 'Incorrect number of symbols returned'); assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'File with symbol not found in child workspace folder'); assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'File with symbol not found in child workspace folder'); const symbolsForMeth = await provider.provideWorkspaceSymbols('meth', new CancellationTokenSource().token); - assert.equal(symbolsForMeth.length, 10, 'Incorrect number of symbols returned'); + assert.equal(symbolsForMeth.length >= 10, true, 'Incorrect number of symbols returned'); assert.notEqual(symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'Symbols not returned for childFile.py'); assert.notEqual(symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'Symbols not returned for workspace2File.py'); assert.notEqual(symbolsForMeth.findIndex(sym => sym.location.uri.fsPath.endsWith('file.py')), -1, 'Symbols not returned for file.py'); From f044e2724f53414f7c9892186bd0fbdf70e6d40a Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 10:38:10 -0700 Subject: [PATCH 29/57] disable all but multiroot tests --- src/test/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index e5fe465f9647..8d8aeaa4828c 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -20,8 +20,8 @@ testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results timeout: 25000, - grep : 'Multiroot', - invert + grep : 'Workspace Symbols', + // invertWorkspace Symbols }); initializePython(); From 99bf348ea2014fb93921c8ea31d7018f7ff33b4a Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 11:38:00 -0700 Subject: [PATCH 30/57] fixed tests --- src/test/index.ts | 4 +-- .../parent/child/.vscode/settings.json | 2 +- src/test/workspaceSymbols/common.ts | 4 +-- src/test/workspaceSymbols/multiroot.test.ts | 28 +++++++------------ src/test/workspaceSymbols/standard.test.ts | 22 +++++---------- 5 files changed, 22 insertions(+), 38 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index 8d8aeaa4828c..e5fe465f9647 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -20,8 +20,8 @@ testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results timeout: 25000, - grep : 'Workspace Symbols', - // invertWorkspace Symbols + grep : 'Multiroot', + invert }); initializePython(); diff --git a/src/test/multiRootWkspc/parent/child/.vscode/settings.json b/src/test/multiRootWkspc/parent/child/.vscode/settings.json index c404e94945a9..b78380782cd9 100644 --- a/src/test/multiRootWkspc/parent/child/.vscode/settings.json +++ b/src/test/multiRootWkspc/parent/child/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.workspaceSymbols.enabled": false + "python.workspaceSymbols.enabled": true } \ No newline at end of file diff --git a/src/test/workspaceSymbols/common.ts b/src/test/workspaceSymbols/common.ts index aa4338db5a0c..f713b64969c3 100644 --- a/src/test/workspaceSymbols/common.ts +++ b/src/test/workspaceSymbols/common.ts @@ -1,8 +1,8 @@ import { ConfigurationTarget, Uri, workspace } from 'vscode'; import { PythonSettings } from '../../client/common/configSettings'; -export async function enableDisableWorkspaceSymbols(resource: Uri, enabled: boolean) { +export async function enableDisableWorkspaceSymbols(resource: Uri, enabled: boolean, configTarget: ConfigurationTarget) { let settings = workspace.getConfiguration('python', resource); - await settings.update('workspaceSymbols.enabled', enabled, ConfigurationTarget.WorkspaceFolder); + await settings.update('workspaceSymbols.enabled', enabled, configTarget); PythonSettings.dispose(); } \ No newline at end of file diff --git a/src/test/workspaceSymbols/multiroot.test.ts b/src/test/workspaceSymbols/multiroot.test.ts index 331ca5d7e337..270920898526 100644 --- a/src/test/workspaceSymbols/multiroot.test.ts +++ b/src/test/workspaceSymbols/multiroot.test.ts @@ -1,37 +1,30 @@ import * as assert from 'assert'; import * as path from 'path'; -import { CancellationTokenSource, ConfigurationTarget, Uri, workspace } from 'vscode'; +import { CancellationTokenSource, ConfigurationTarget, Uri } from 'vscode'; import { initialize, closeActiveWindows } from '../initialize'; -import { PythonSettings } from '../../client/common/configSettings'; import { Generator } from '../../client/workspaceSymbols/generator'; import { MockOutputChannel } from '../mockClasses'; import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; import { enableDisableWorkspaceSymbols } from './common'; +import { PythonSettings } from '../../client/common/configSettings'; const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); suite('Multiroot Workspace Symbols', () => { suiteSetup(() => initialize()); + setup(() => PythonSettings.dispose()); suiteTeardown(() => closeActiveWindows()); teardown(async () => { await closeActiveWindows(); - await resetSettings(); + await enableDisableWorkspaceSymbols(Uri.file(path.join(multirootPath, 'parent', 'child')), false, ConfigurationTarget.WorkspaceFolder); + await enableDisableWorkspaceSymbols(Uri.file(path.join(multirootPath, 'workspace2')), false, ConfigurationTarget.WorkspaceFolder); }); - async function resetSettings() { - PythonSettings.dispose(); - const childWorkspaceUri = Uri.file(path.join(multirootPath, 'parent', 'child')); - const settings = workspace.getConfiguration('python', childWorkspaceUri); - const value = settings.inspect('workspaceSymbols.enabled'); - if (value.workspaceFolderValue !== false) { - await settings.update('workspaceSymbols.enabled', false, ConfigurationTarget.WorkspaceFolder); - } - } test(`symbols should be returned when enabeld and vice versa`, async () => { const childWorkspaceUri = Uri.file(path.join(multirootPath, 'parent', 'child')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(childWorkspaceUri, false); + await enableDisableWorkspaceSymbols(childWorkspaceUri, false, ConfigurationTarget.WorkspaceFolder); let generator = new Generator(childWorkspaceUri, outputChannel); let provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -39,7 +32,7 @@ suite('Multiroot Workspace Symbols', () => { assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); generator.dispose(); - await enableDisableWorkspaceSymbols(childWorkspaceUri, true); + await enableDisableWorkspaceSymbols(childWorkspaceUri, true, ConfigurationTarget.WorkspaceFolder); generator = new Generator(childWorkspaceUri, outputChannel); provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -51,8 +44,8 @@ suite('Multiroot Workspace Symbols', () => { const workspace2Uri = Uri.file(path.join(multirootPath, 'workspace2')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(childWorkspaceUri, true); - await enableDisableWorkspaceSymbols(workspace2Uri, true); + await enableDisableWorkspaceSymbols(childWorkspaceUri, true, ConfigurationTarget.WorkspaceFolder); + await enableDisableWorkspaceSymbols(workspace2Uri, true, ConfigurationTarget.WorkspaceFolder); const generators = [ new Generator(childWorkspaceUri, outputChannel), @@ -60,8 +53,7 @@ suite('Multiroot Workspace Symbols', () => { const provider = new WorkspaceSymbolProvider(generators, outputChannel); const symbols = await provider.provideWorkspaceSymbols('meth1Of', new CancellationTokenSource().token); - // Remember (multiroot workspace is a child directory, hence we have more python files to account for in there) - assert.equal(symbols.length >= 2, true, 'Incorrect number of symbols returned'); + assert.equal(symbols.length, 2, 'Incorrect number of symbols returned'); assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('childFile.py')), -1, 'File with symbol not found in child workspace folder'); assert.notEqual(symbols.findIndex(sym => sym.location.uri.fsPath.endsWith('workspace2File.py')), -1, 'File with symbol not found in child workspace folder'); }); diff --git a/src/test/workspaceSymbols/standard.test.ts b/src/test/workspaceSymbols/standard.test.ts index 68cc5d00184e..f7e0270d4fd9 100644 --- a/src/test/workspaceSymbols/standard.test.ts +++ b/src/test/workspaceSymbols/standard.test.ts @@ -1,37 +1,29 @@ import * as assert from 'assert'; import * as path from 'path'; -import { CancellationTokenSource, ConfigurationTarget, Uri, workspace } from 'vscode'; +import { CancellationTokenSource, ConfigurationTarget, Uri } from 'vscode'; import { initialize, closeActiveWindows } from '../initialize'; -import { PythonSettings } from '../../client/common/configSettings'; import { Generator } from '../../client/workspaceSymbols/generator'; import { MockOutputChannel } from '../mockClasses'; import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; import { enableDisableWorkspaceSymbols } from './common'; +import { PythonSettings } from '../../client/common/configSettings'; const symbolFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'symbolFiles'); suite('Workspace Symbols', () => { suiteSetup(() => initialize()); suiteTeardown(() => closeActiveWindows()); + setup(() => PythonSettings.dispose()); teardown(async () => { await closeActiveWindows(); - await resetSettings(); + await enableDisableWorkspaceSymbols(Uri.file(path.join(symbolFilesPath, 'file.py')), false, ConfigurationTarget.Workspace); }); - async function resetSettings() { - PythonSettings.dispose(); - const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); - const settings = workspace.getConfiguration('python', workspaceUri); - const value = settings.inspect('workspaceSymbols.enabled'); - if (value.workspaceFolderValue !== true) { - await settings.update('workspaceSymbols.enabled', true, ConfigurationTarget.Workspace); - } - } test(`symbols should be returned when enabeld and vice versa`, async () => { const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(workspaceUri, false); + await enableDisableWorkspaceSymbols(workspaceUri, false, ConfigurationTarget.Workspace); let generator = new Generator(workspaceUri, outputChannel); let provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -39,7 +31,7 @@ suite('Workspace Symbols', () => { assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); generator.dispose(); - await enableDisableWorkspaceSymbols(workspaceUri, true); + await enableDisableWorkspaceSymbols(workspaceUri, true, ConfigurationTarget.Workspace); generator = new Generator(workspaceUri, outputChannel); provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -50,7 +42,7 @@ suite('Workspace Symbols', () => { const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(workspaceUri, true); + await enableDisableWorkspaceSymbols(workspaceUri, true, ConfigurationTarget.Workspace); const generators = [new Generator(workspaceUri, outputChannel)]; const provider = new WorkspaceSymbolProvider(generators, outputChannel); From 45ac933d224b721239eecee97f5de75ab956adf2 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 13:28:36 -0700 Subject: [PATCH 31/57] fix tests --- .travis.yml | 1 + .vscode/launch.json | 2 +- src/test/autocomplete/base.test.ts | 4 +- src/test/autocomplete/pep484.test.ts | 3 +- src/test/autocomplete/pep526.test.ts | 3 +- src/test/common.ts | 20 +++++ .../common/configSettings.multiroot.test.ts | 5 +- src/test/common/installer.test.ts | 3 +- src/test/definitions/hover.test.ts | 3 +- src/test/format/extension.format.test.ts | 3 +- .../format/extension.onTypeFormat.test.ts | 3 +- src/test/format/extension.sort.test.ts | 3 +- src/test/index.ts | 8 +- src/test/initialize.ts | 44 +++++++---- .../interpreters/condaEnvFileService.test.ts | 3 +- src/test/interpreters/condaEnvService.test.ts | 22 ++---- src/test/interpreters/display.test.ts | 45 ++++------- .../windowsRegistryService.test.ts | 15 +--- src/test/jupyter/jupyter.codeHelper.test.ts | 5 +- src/test/jupyter/jupyterClient.test.ts | 3 +- src/test/jupyter/jupyterKernel.test.ts | 3 +- src/test/jupyter/jupyterKernelManager.test.ts | 3 +- src/test/linters/lint.multiroot.test.ts | 12 ++- src/test/linters/lint.test.ts | 79 ++++++++----------- src/test/multiRootTest.ts | 2 +- .../parent/child/.vscode/settings.json | 3 - .../workspace2/workspace2.tags.file | 24 ------ .../shebangCodeLenseProvider.test.ts | 29 +++---- .../extension.refactor.extract.method.test.ts | 22 +++--- .../extension.refactor.extract.var.test.ts | 24 +++--- src/test/unittests/nosetest.test.ts | 27 +++---- src/test/unittests/pytest.test.ts | 50 +++++------- src/test/unittests/unittest.test.ts | 64 ++++++--------- src/test/workspaceSymbols/common.ts | 8 -- src/test/workspaceSymbols/multiroot.test.ts | 21 +++-- src/test/workspaceSymbols/standard.test.ts | 14 ++-- .../disableLinters/.vscode/tags | 0 .../disableLinters/file.py | 0 .../multi.code-workspace | 6 +- .../parent/child/.vscode/settings.json | 3 + .../parent/child/.vscode/tags | 0 .../parent/child/childFile.py | 0 .../parent/child/file.py | 0 .../workspace1/.vscode/settings.json | 0 .../workspace1/.vscode/tags | 0 .../workspace1/file.py | 0 .../workspace2/.vscode/settings.json | 2 +- .../workspace2/file.py | 0 .../workspace2/workspace2.tags.file | 24 ++++++ .../workspace2/workspace2File.py | 0 .../workspace3/.vscode/settings.json | 0 .../workspace3/file.py | 0 .../workspace3/workspace3.tags.file | 24 +++--- 53 files changed, 302 insertions(+), 340 deletions(-) create mode 100644 src/test/common.ts delete mode 100644 src/test/multiRootWkspc/parent/child/.vscode/settings.json delete mode 100644 src/test/multiRootWkspc/workspace2/workspace2.tags.file delete mode 100644 src/test/workspaceSymbols/common.ts rename src/{test/multiRootWkspc => testMultiRootWkspc}/disableLinters/.vscode/tags (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/disableLinters/file.py (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/multi.code-workspace (82%) create mode 100644 src/testMultiRootWkspc/parent/child/.vscode/settings.json rename src/{test/multiRootWkspc => testMultiRootWkspc}/parent/child/.vscode/tags (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/parent/child/childFile.py (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/parent/child/file.py (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace1/.vscode/settings.json (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace1/.vscode/tags (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace1/file.py (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace2/.vscode/settings.json (66%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace2/file.py (100%) create mode 100644 src/testMultiRootWkspc/workspace2/workspace2.tags.file rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace2/workspace2File.py (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace3/.vscode/settings.json (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace3/file.py (100%) rename src/{test/multiRootWkspc => testMultiRootWkspc}/workspace3/workspace3.tags.file (51%) diff --git a/.travis.yml b/.travis.yml index 45c73062ebf5..4631c982bbdb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,6 +48,7 @@ before_install: | pyenv install $PYTHON pyenv global $PYTHON fi + export TRAVIS_PYTHON_PATH=`which python` install: - pip install --upgrade -r requirements.txt - npm install diff --git a/.vscode/launch.json b/.vscode/launch.json index 6d4317be09b1..7813c5fc06c0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -73,7 +73,7 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "${workspaceRoot}/src/test/multiRootWkspc/multi.code-workspace", + "${workspaceRoot}/src/testMultiRootWkspc/multi.code-workspace", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], diff --git a/src/test/autocomplete/base.test.ts b/src/test/autocomplete/base.test.ts index 83123b308f91..a1e3feeb9491 100644 --- a/src/test/autocomplete/base.test.ts +++ b/src/test/autocomplete/base.test.ts @@ -10,7 +10,7 @@ import { EOL } from 'os'; import * as vscode from 'vscode'; import * as path from 'path'; import * as settings from '../../client/common/configSettings'; -import { initialize, closeActiveWindows } from '../initialize'; +import { initialize, closeActiveWindows, initializeTest } from '../initialize'; import { execPythonFile } from '../../client/common/utils'; const pythonSettings = settings.PythonSettings.getInstance(); @@ -30,7 +30,7 @@ suite('Autocomplete', () => { let version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); - + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(() => closeActiveWindows()); diff --git a/src/test/autocomplete/pep484.test.ts b/src/test/autocomplete/pep484.test.ts index 6108b3e8c1b4..546c26d1253d 100644 --- a/src/test/autocomplete/pep484.test.ts +++ b/src/test/autocomplete/pep484.test.ts @@ -11,7 +11,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { execPythonFile } from '../../client/common/utils'; -import { initialize, closeActiveWindows } from '../initialize'; +import { initialize, closeActiveWindows, initializeTest } from '../initialize'; const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); @@ -24,6 +24,7 @@ suite('Autocomplete PEP 484', () => { const version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(() => closeActiveWindows()); diff --git a/src/test/autocomplete/pep526.test.ts b/src/test/autocomplete/pep526.test.ts index 1d09d8168682..b7c4b37de08a 100644 --- a/src/test/autocomplete/pep526.test.ts +++ b/src/test/autocomplete/pep526.test.ts @@ -11,7 +11,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { execPythonFile } from '../../client/common/utils'; -import { initialize, closeActiveWindows } from '../initialize'; +import { initialize, closeActiveWindows, initializeTest } from '../initialize'; const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); @@ -24,6 +24,7 @@ suite('Autocomplete PEP 526', () => { const version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(() => closeActiveWindows()); diff --git a/src/test/common.ts b/src/test/common.ts new file mode 100644 index 000000000000..1434e3cdf5ef --- /dev/null +++ b/src/test/common.ts @@ -0,0 +1,20 @@ +import * as path from 'path'; +import { ConfigurationTarget, Uri, workspace } from 'vscode'; +import { PythonSettings } from '../client/common/configSettings'; + +export const fileInNonRootWorkspace = path.join(__dirname, '..', '..', 'src', 'test', 'symbolFiles'); +export const rootWorkspaceUri = workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; + +export type PythonSettingKeys = 'workspaceSymbols.enabled' | 'pythonPath' | + 'linting.lintOnSave' | 'linting.lintOnTextChange' | + 'linting.enabled' | 'linting.pylintEnabled' | + 'linting.flake8Enabled' | 'linting.pep8Enabled' | + 'linting.prospectorEnabled' | 'linting.pydocstyleEnabled' | + 'unitTest.nosetestArgs' | 'unitTest.pyTestArgs' | 'unitTest.unittestArgs'; + + +export async function updateSetting(setting: PythonSettingKeys, value: any, resource: Uri, configTarget: ConfigurationTarget) { + let settings = workspace.getConfiguration('python', resource); + await settings.update(setting, value, configTarget); + PythonSettings.dispose(); +} \ No newline at end of file diff --git a/src/test/common/configSettings.multiroot.test.ts b/src/test/common/configSettings.multiroot.test.ts index 4157903d28df..cce4dc99eddf 100644 --- a/src/test/common/configSettings.multiroot.test.ts +++ b/src/test/common/configSettings.multiroot.test.ts @@ -1,16 +1,17 @@ import * as assert from 'assert'; import * as path from 'path'; import { ConfigurationTarget, Uri, workspace } from 'vscode'; -import { initialize, closeActiveWindows } from '../initialize'; +import { initialize, closeActiveWindows, initializeTest } from '../initialize'; import { PythonSettings } from '../../client/common/configSettings'; -const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); +const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'testMultiRootWkspc'); suite('Multiroot Config Settings', () => { suiteSetup(async () => { await initialize(); await resetSettings(); }); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(async () => { await resetSettings(); diff --git a/src/test/common/installer.test.ts b/src/test/common/installer.test.ts index 4f30f16e05bc..d49b32c0a066 100644 --- a/src/test/common/installer.test.ts +++ b/src/test/common/installer.test.ts @@ -1,5 +1,5 @@ import * as assert from 'assert'; -import { closeActiveWindows, IS_TRAVIS } from './../initialize'; +import { closeActiveWindows, IS_TRAVIS, initializeTest } from './../initialize'; import { MockOutputChannel } from './../mockClasses'; import { Installer, Product } from '../../client/common/installer'; import { EnumEx } from '../../client/common/enumUtils'; @@ -15,6 +15,7 @@ suite('Installer', () => { outputChannel = new MockOutputChannel('Installer'); installer = new Installer(outputChannel); }); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(() => closeActiveWindows()); diff --git a/src/test/definitions/hover.test.ts b/src/test/definitions/hover.test.ts index 194d325edab6..07f06b8843ca 100644 --- a/src/test/definitions/hover.test.ts +++ b/src/test/definitions/hover.test.ts @@ -9,7 +9,7 @@ import { EOL } from 'os'; // as well as import your extension to test it import * as vscode from 'vscode'; import * as path from 'path'; -import { initialize, closeActiveWindows } from '../initialize'; +import { initialize, closeActiveWindows, initializeTest } from '../initialize'; import { normalizeMarkedString } from '../textUtils'; const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); @@ -23,6 +23,7 @@ const fileStringFormat = path.join(hoverPath, 'stringFormat.py'); suite('Hover Definition', () => { suiteSetup(() => initialize()); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(() => closeActiveWindows()); diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index 986d64e976a9..8d8cb1fb866a 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -14,7 +14,7 @@ import * as settings from '../../client/common/configSettings'; import * as fs from 'fs-extra'; import { EOL } from 'os'; import { AutoPep8Formatter } from '../../client/formatters/autoPep8Formatter'; -import { initialize, IS_TRAVIS, closeActiveWindows } from '../initialize'; +import { initialize, IS_TRAVIS, closeActiveWindows, initializeTest } from '../initialize'; import { YapfFormatter } from '../../client/formatters/yapfFormatter'; import { execPythonFile } from '../../client/common/utils'; @@ -47,6 +47,7 @@ suite('Formatting', () => { formattedAutoPep8 = formattedResults[1]; }).then(() => { }); }); + setup(() => initializeTest()); suiteTeardown(() => { [autoPep8FileToFormat, autoPep8FileToAutoFormat, yapfFileToFormat, yapfFileToAutoFormat].forEach(file => { if (fs.existsSync(file)) { diff --git a/src/test/format/extension.onTypeFormat.test.ts b/src/test/format/extension.onTypeFormat.test.ts index 4ede98a39bac..097922e9010e 100644 --- a/src/test/format/extension.onTypeFormat.test.ts +++ b/src/test/format/extension.onTypeFormat.test.ts @@ -11,7 +11,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs-extra'; -import { initialize, closeActiveWindows } from '../initialize'; +import { initialize, closeActiveWindows, initializeTest } from '../initialize'; import { BlockFormatProviders } from '../../client/typeFormatters/blockFormatProvider'; const srcPythoFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'typeFormatFiles'); @@ -676,6 +676,7 @@ suite('Else blocks with indentation of Tab', () => { fs.copySync(path.join(srcPythoFilesPath, file), targetFile); }); }); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(() => closeActiveWindows()); diff --git a/src/test/format/extension.sort.test.ts b/src/test/format/extension.sort.test.ts index 15b1f0898e57..c8a427a6b177 100644 --- a/src/test/format/extension.sort.test.ts +++ b/src/test/format/extension.sort.test.ts @@ -14,7 +14,7 @@ import * as settings from '../../client/common/configSettings'; import * as fs from 'fs'; import { EOL } from 'os'; import { PythonImportSortProvider } from '../../client/providers/importSortProvider'; -import { initialize, IS_TRAVIS, closeActiveWindows } from '../initialize'; +import { initialize, IS_TRAVIS, closeActiveWindows, initializeTest } from '../initialize'; const pythonSettings = settings.PythonSettings.getInstance(); @@ -29,6 +29,7 @@ const extensionDir = path.join(__dirname, '..', '..', '..'); suite('Sorting', () => { suiteSetup(() => initialize()); + setup(() => initializeTest()); suiteTeardown(() => { fs.writeFileSync(fileToFormatWithConfig, fs.readFileSync(originalFileToFormatWithConfig)); fs.writeFileSync(fileToFormatWithConfig1, fs.readFileSync(originalFileToFormatWithConfig1)); diff --git a/src/test/index.ts b/src/test/index.ts index e5fe465f9647..526834d62019 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -1,4 +1,4 @@ -import { initializePython, isMultitrootTest } from './initialize'; +import { initializePython, IS_MULTI_ROOT_TEST } from './initialize'; // // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING // @@ -12,7 +12,7 @@ import { initializePython, isMultitrootTest } from './initialize'; // a possible error to the callback or null if none. const testRunner = require('vscode/lib/testrunner'); -const invert = isMultitrootTest() ? undefined : 'invert'; +const invert = IS_MULTI_ROOT_TEST ? undefined : 'invert'; // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info @@ -20,8 +20,8 @@ testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results timeout: 25000, - grep : 'Multiroot', - invert + grep : 'ChildProc', + // invert }); initializePython(); diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 97b22674894f..7303b5b9353f 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -16,12 +16,28 @@ import * as vscode from "vscode"; import * as path from "path"; let dummyPythonFile = path.join(__dirname, "..", "..", "src", "test", "pythonFiles", "dummy.py"); -export function initialize(): Promise { +let configSettings: any = undefined; +export async function initialize(): Promise { + await initializePython(); + if (!configSettings) { + configSettings = await require('../client/common/configSettings'); + } + // Dispose any cached python settings (used only in test env) + configSettings.PythonSettings.dispose(); // Opening a python file activates the extension - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { vscode.workspace.openTextDocument(dummyPythonFile).then(() => resolve(), reject); }); } +export async function initializeTest(): Promise { + await initializePython(); + await closeActiveWindows(); + if (!configSettings) { + configSettings = await require('../client/common/configSettings'); + } + // Dispose any cached python settings (used only in test env) + configSettings.PythonSettings.dispose(); +} export async function wait(timeoutMilliseconds: number) { return new Promise(resolve => { @@ -64,25 +80,22 @@ export async function closeActiveWindows(): Promise { }); } -export const IS_TRAVIS = (process.env['TRAVIS'] + '') === 'true'; -export const TEST_TIMEOUT = 25000; function getPythonPath(): string { - const pythonPaths = ['/home/travis/virtualenv/python3.5.2/bin/python', - '/xUsers/travis/.pyenv/versions/3.5.1/envs/MYVERSION/bin/python', - '/xUsers/donjayamanne/Projects/PythonEnvs/p361/bin/python', - 'cC:/Users/dojayama/nine/python.exe', - 'C:/Development/PythonEnvs/p27/scripts/python.exe', - '/Users/donjayamanne/Projects/PythonEnvs/p27/bin/python']; - for (let counter = 0; counter < pythonPaths.length; counter++) { - if (fs.existsSync(pythonPaths[counter])) { - return pythonPaths[counter]; - } + if (process.env.TRAVIS_PYTHON_PATH && fs.existsSync(process.env.TRAVIS_PYTHON_PATH)) { + return process.env.TRAVIS_PYTHON_PATH; } return 'python'; } +function isMultitrootTest() { + return Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 1; +} + const PYTHON_PATH = getPythonPath(); +export const IS_TRAVIS = (process.env['TRAVIS'] + '') === 'true'; +export const TEST_TIMEOUT = 25000; +export const IS_MULTI_ROOT_TEST = isMultitrootTest(); // Ability to use custom python environments for testing export function initializePython() { @@ -90,6 +103,3 @@ export function initializePython() { pythonConfig.update('pythonPath', PYTHON_PATH); } -export function isMultitrootTest() { - return Array.isArray(vscode.workspace.workspaceFolders) && vscode.workspace.workspaceFolders.length > 1; -} diff --git a/src/test/interpreters/condaEnvFileService.test.ts b/src/test/interpreters/condaEnvFileService.test.ts index 9778179149c8..26bd45c9928d 100644 --- a/src/test/interpreters/condaEnvFileService.test.ts +++ b/src/test/interpreters/condaEnvFileService.test.ts @@ -2,7 +2,7 @@ import * as assert from 'assert'; import * as path from 'path'; import * as fs from 'fs-extra'; import { EOL } from 'os'; -import { initialize } from '../initialize'; +import { initialize, initializeTest } from '../initialize'; import { IS_WINDOWS } from '../../client/common/utils'; import { MockInterpreterVersionProvider } from './mocks'; import { CondaEnvFileService } from '../../client/interpreter/locators/services/condaEnvFileService'; @@ -18,6 +18,7 @@ const environmentsFilePath = path.join(environmentsPath, 'environments.txt'); suite('Interpreters from Conda Environments Text File', () => { suiteSetup(() => initialize()); + setup(() => initializeTest()); suiteTeardown(async () => { // Clear the file so we don't get unwanted changes prompting for a checkin of this file await updateEnvWithInterpreters([]); diff --git a/src/test/interpreters/condaEnvService.test.ts b/src/test/interpreters/condaEnvService.test.ts index 0441b93cd7f4..823f773ede6c 100644 --- a/src/test/interpreters/condaEnvService.test.ts +++ b/src/test/interpreters/condaEnvService.test.ts @@ -1,26 +1,18 @@ import * as assert from 'assert'; import * as path from 'path'; -import * as settings from '../../client/common/configSettings'; -import { initialize } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { initialize, initializeTest } from '../initialize'; import { IS_WINDOWS } from '../../client/common/utils'; import { CondaEnvService } from '../../client/interpreter/locators/services/condaEnvService'; import { AnacondaCompanyName } from '../../client/interpreter/locators/services/conda'; import { MockProvider } from './mocks'; import { PythonInterpreter } from '../../client/interpreter/contracts'; -const pythonSettings = settings.PythonSettings.getInstance(); const environmentsPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'environments'); -let originalPythonPath; suite('Interpreters from Conda Environments', () => { - suiteSetup(() => { - originalPythonPath = pythonSettings.pythonPath; - return initialize(); - }); - teardown(() => { - pythonSettings.pythonPath = originalPythonPath; - }); - + suiteSetup(() => initialize()); + setup(() => initializeTest()); test('Must return an empty list for empty json', async () => { const condaProvider = new CondaEnvService(); const interpreters = await condaProvider.parseCondaInfo({} as any) @@ -113,7 +105,7 @@ suite('Interpreters from Conda Environments', () => { test('Must detect conda environments from a list', async () => { const registryInterpreters: PythonInterpreter[] = [ { displayName: 'One', path: 'c:/path1/one.exe', companyDisplayName: 'One 1' }, - { displayName: 'Two', path: pythonSettings.pythonPath, companyDisplayName: 'Two 2' }, + { displayName: 'Two', path: PythonSettings.getInstance().pythonPath, companyDisplayName: 'Two 2' }, { displayName: 'Three', path: path.join(environmentsPath, 'path1', 'one.exe'), companyDisplayName: 'Three 3' }, { displayName: 'Anaconda', path: path.join(environmentsPath, 'path2', 'one.exe'), companyDisplayName: 'Three 3' }, { displayName: 'xAnaconda', path: path.join(environmentsPath, 'path2', 'one.exe'), companyDisplayName: 'Three 3' }, @@ -134,7 +126,7 @@ suite('Interpreters from Conda Environments', () => { test('Correctly identifies latest version when major version is different', async () => { const registryInterpreters: PythonInterpreter[] = [ { displayName: 'One', path: path.join(environmentsPath, 'path1', 'one.exe'), companyDisplayName: 'One 1', version: '1' }, - { displayName: 'Two', path: pythonSettings.pythonPath, companyDisplayName: 'Two 2', version: '3.1.3' }, + { displayName: 'Two', path: PythonSettings.getInstance().pythonPath, companyDisplayName: 'Two 2', version: '3.1.3' }, { displayName: 'Three', path: path.join(environmentsPath, 'path2', 'one.exe'), companyDisplayName: 'Three 3', version: '2.10.1' }, { displayName: 'Four', path: path.join(environmentsPath, 'conda', 'envs', 'scipy'), companyDisplayName: 'Three 3', version: null }, { displayName: 'Five', path: path.join(environmentsPath, 'conda', 'envs', 'numpy'), companyDisplayName: 'Three 3', version: undefined }, @@ -149,7 +141,7 @@ suite('Interpreters from Conda Environments', () => { test('Correctly identifies latest version when major version is same', async () => { const registryInterpreters: PythonInterpreter[] = [ { displayName: 'One', path: path.join(environmentsPath, 'path1', 'one.exe'), companyDisplayName: 'One 1', version: '1' }, - { displayName: 'Two', path: pythonSettings.pythonPath, companyDisplayName: 'Two 2', version: '2.11.3' }, + { displayName: 'Two', path: PythonSettings.getInstance().pythonPath, companyDisplayName: 'Two 2', version: '2.11.3' }, { displayName: 'Three', path: path.join(environmentsPath, 'path2', 'one.exe'), companyDisplayName: 'Three 3', version: '2.10.1' }, { displayName: 'Four', path: path.join(environmentsPath, 'conda', 'envs', 'scipy'), companyDisplayName: 'Three 3', version: null }, { displayName: 'Five', path: path.join(environmentsPath, 'conda', 'envs', 'numpy'), companyDisplayName: 'Three 3', version: undefined }, diff --git a/src/test/interpreters/display.test.ts b/src/test/interpreters/display.test.ts index 2f38d272125a..203c1b7f4d2b 100644 --- a/src/test/interpreters/display.test.ts +++ b/src/test/interpreters/display.test.ts @@ -1,9 +1,8 @@ +import { PythonSettings } from '../../client/common/configSettings'; import * as assert from 'assert'; import * as child_process from 'child_process'; -import * as settings from '../../client/common/configSettings'; import * as path from 'path'; -import * as utils from '../../client/common/utils'; -import { initialize } from '../initialize'; +import { closeActiveWindows, initialize, initializeTest } from '../initialize'; import { MockStatusBarItem } from '../mockClasses'; import { MockInterpreterVersionProvider } from './mocks'; import { InterpreterDisplay } from '../../client/interpreter/display'; @@ -11,17 +10,15 @@ import { MockProvider, MockVirtualEnv } from './mocks'; import { EOL } from 'os'; import { VirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs'; import { getFirstNonEmptyLineFromMultilineString } from '../../client/interpreter/helpers'; - -let pythonSettings = settings.PythonSettings.getInstance(); -let originalPythonPath; +import { rootWorkspaceUri, updateSetting } from '../common'; +import { ConfigurationTarget } from 'vscode'; suite('Interpreters Display', () => { - suiteSetup(() => { - originalPythonPath = pythonSettings.pythonPath; - return initialize(); - }); - teardown(() => { - pythonSettings.pythonPath = originalPythonPath; + suiteSetup(() => initialize()); + setup(() => initializeTest()); + teardown(async () => { + await initialize(); + await closeActiveWindows(); }); test('Must have command name', () => { @@ -59,7 +56,8 @@ suite('Interpreters Display', () => { const displayNameProvider = new MockInterpreterVersionProvider(displayName, true); const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value - const pythonPath = pythonSettings.pythonPath = 'c:/some/unknonw/Python Interpreter.exe'; + const pythonPath = 'c:/some/unknonw/Python Interpreter.exe'; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); await display.refresh(); const defaultDisplayName = `${path.basename(pythonPath)} [Environment]`; @@ -67,10 +65,10 @@ suite('Interpreters Display', () => { }); test('Must get display name from a list of interpreters', async () => { const pythonPath = await new Promise(resolve => { - child_process.execFile(pythonSettings.pythonPath, ["-c", "import sys;print(sys.executable)"], (_, stdout) => { + child_process.execFile(PythonSettings.getInstance().pythonPath, ["-c", "import sys;print(sys.executable)"], (_, stdout) => { resolve(getFirstNonEmptyLineFromMultilineString(stdout)); }); - }).then(value => value.length === 0 ? pythonSettings.pythonPath : value); + }).then(value => value.length === 0 ? PythonSettings.getInstance().pythonPath : value); const statusBar = new MockStatusBarItem(); const interpreters = [ { displayName: 'One', path: 'c:/path1/one.exe', type: 'One 1' }, @@ -87,10 +85,10 @@ suite('Interpreters Display', () => { }); test('Must suffix tooltip with the companyDisplayName of interpreter', async () => { const pythonPath = await new Promise(resolve => { - child_process.execFile(pythonSettings.pythonPath, ["-c", "import sys;print(sys.executable)"], (_, stdout) => { + child_process.execFile(PythonSettings.getInstance().pythonPath, ["-c", "import sys;print(sys.executable)"], (_, stdout) => { resolve(getFirstNonEmptyLineFromMultilineString(stdout)); }); - }).then(value => value.length === 0 ? pythonSettings.pythonPath : value); + }).then(value => value.length === 0 ? PythonSettings.getInstance().pythonPath : value); const statusBar = new MockStatusBarItem(); const interpreters = [ @@ -117,19 +115,10 @@ suite('Interpreters Display', () => { const displayNameProvider = new MockInterpreterVersionProvider('', true); const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value - pythonSettings.pythonPath = 'c:/some/unknonw/Python Interpreter.exe'; + const pythonPath = 'c:/some/unknonw/Python Interpreter.exe'; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); await display.refresh(); assert.equal(statusBar.text, '$(alert) Select Python Environment', 'Incorrect display name'); }); }); - -async function getInterpreterDisplayName(pythonPath: string, defaultValue: string) { - return utils.execPythonFile(pythonPath, ['--version'], __dirname, true) - .then(version => { - version = version.split(/\r?\n/g).map(line => line.trim()).filter(line => line.length > 0).join(''); - return version.length > 0 ? version : defaultValue; - }) - .catch(() => defaultValue); -} - diff --git a/src/test/interpreters/windowsRegistryService.test.ts b/src/test/interpreters/windowsRegistryService.test.ts index 71a68c62f4bc..aedad057a4d0 100644 --- a/src/test/interpreters/windowsRegistryService.test.ts +++ b/src/test/interpreters/windowsRegistryService.test.ts @@ -1,25 +1,16 @@ import * as assert from 'assert'; import * as path from 'path'; -import * as settings from '../../client/common/configSettings'; -import { initialize } from '../initialize'; +import { initialize, initializeTest } from '../initialize'; import { IS_WINDOWS } from '../../client/debugger/Common/Utils'; import { WindowsRegistryService } from '../../client/interpreter/locators/services/windowsRegistryService'; import { MockRegistry } from './mocks'; import { Architecture, Hive } from '../../client/common/registry'; -const pythonSettings = settings.PythonSettings.getInstance(); const environmentsPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'environments'); -let originalPythonPath; suite('Interpreters from Windows Registry', () => { - suiteSetup(() => { - originalPythonPath = pythonSettings.pythonPath; - return initialize(); - }); - teardown(() => { - pythonSettings.pythonPath = originalPythonPath; - }); - + suiteSetup(() => initialize()); + setup(() => initializeTest()); if (IS_WINDOWS) { test('Must return an empty list (x86)', async () => { const registry = new MockRegistry([], []); diff --git a/src/test/jupyter/jupyter.codeHelper.test.ts b/src/test/jupyter/jupyter.codeHelper.test.ts index b0a25bb906c3..60643f2d6b2b 100644 --- a/src/test/jupyter/jupyter.codeHelper.test.ts +++ b/src/test/jupyter/jupyter.codeHelper.test.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import * as path from 'path'; -import { initialize, closeActiveWindows } from './../initialize'; +import { initialize, closeActiveWindows, initializeTest } from './../initialize'; import { CodeHelper } from '../../client/jupyter/common/codeHelper'; import { JupyterCodeLensProvider } from '../../client/jupyter/editorIntegration/codeLensProvider'; @@ -9,8 +9,7 @@ const FILE_WITH_CELLS = path.join(__dirname, '..', '..', '..', 'src', 'test', 'p suite('Jupyter Code Helper', () => { suiteSetup(() => initialize()); - - setup(() => closeActiveWindows()); + setup(() => initializeTest()); teardown(() => closeActiveWindows()); const codeLensProvider = new JupyterCodeLensProvider(); const codeHelper = new CodeHelper(codeLensProvider); diff --git a/src/test/jupyter/jupyterClient.test.ts b/src/test/jupyter/jupyterClient.test.ts index 5250443df1be..c74d027db47c 100644 --- a/src/test/jupyter/jupyterClient.test.ts +++ b/src/test/jupyter/jupyterClient.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; import { MockOutputChannel } from './mocks'; -import { initialize } from './../initialize'; +import { initialize, initializeTest } from './../initialize'; import { JupyterClientAdapter } from '../../client/jupyter/jupyter_client/main'; import { KernelRestartedError, KernelShutdownError } from '../../client/jupyter/common/errors'; import { createDeferred } from '../../client/common/helpers'; @@ -13,6 +13,7 @@ suite('JupyterClient', () => { process.env['DEBUG_DJAYAMANNE_IPYTHON'] = '1'; output = new MockOutputChannel('Jupyter'); jupyter = new JupyterClientAdapter(output, __dirname); + return initializeTest(); }); teardown(() => { process.env['PYTHON_DONJAYAMANNE_TEST'] = '1'; diff --git a/src/test/jupyter/jupyterKernel.test.ts b/src/test/jupyter/jupyterKernel.test.ts index 3893178d74ec..fddedbe166c1 100644 --- a/src/test/jupyter/jupyterKernel.test.ts +++ b/src/test/jupyter/jupyterKernel.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; import { MockOutputChannel } from './mocks'; -import { initialize } from './../initialize'; +import { initialize, initializeTest } from './../initialize'; import { JupyterClientAdapter } from '../../client/jupyter/jupyter_client/main'; import { KernelShutdownError } from '../../client/jupyter/common/errors'; import { createDeferred } from '../../client/common/helpers'; @@ -17,6 +17,7 @@ suite('Jupyter Kernel', () => { disposables.push(output); jupyter = new JupyterClientAdapter(output, __dirname); disposables.push(jupyter); + return initializeTest(); }); teardown(() => { process.env['PYTHON_DONJAYAMANNE_TEST'] = '1'; diff --git a/src/test/jupyter/jupyterKernelManager.test.ts b/src/test/jupyter/jupyterKernelManager.test.ts index b4581e6889c1..f4c5e9f51c4f 100644 --- a/src/test/jupyter/jupyterKernelManager.test.ts +++ b/src/test/jupyter/jupyterKernelManager.test.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { MockOutputChannel } from './mocks'; -import { initialize } from './../initialize'; +import { initialize, initializeTest } from './../initialize'; import { JupyterClientAdapter } from '../../client/jupyter/jupyter_client/main'; import { KernelManagerImpl } from '../../client/jupyter/kernel-manager'; @@ -17,6 +17,7 @@ suite('Kernel Manager', () => { disposables.push(jupyter); // Hack hack hack hack hack :) cmds.registerCommand = function () { }; + return initializeTest(); }); teardown(() => { process.env['PYTHON_DONJAYAMANNE_TEST'] = '1'; diff --git a/src/test/linters/lint.multiroot.test.ts b/src/test/linters/lint.multiroot.test.ts index e038ac9a1339..17089260ab49 100644 --- a/src/test/linters/lint.multiroot.test.ts +++ b/src/test/linters/lint.multiroot.test.ts @@ -3,18 +3,16 @@ import * as path from 'path'; import * as baseLinter from '../../client/linters/baseLinter'; import * as pyLint from '../../client/linters/pylint'; import * as flake8 from '../../client/linters/flake8'; -import { PythonSettings } from '../../client/common/configSettings'; import { CancellationTokenSource, ConfigurationTarget, Uri, window, workspace } from 'vscode'; -import { initialize, closeActiveWindows } from '../initialize'; +import { closeActiveWindows, initialize, initializeTest } from '../initialize'; import { MockOutputChannel } from '../mockClasses'; +import { PythonSettings } from '../../client/common/configSettings'; -const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); +const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'testMultiRootWkspc'); suite('Multiroot Linting', () => { - suiteSetup(async () => { - await initialize(); - PythonSettings.dispose(); - }); + suiteSetup(() => initialize()); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(async () => { await closeActiveWindows(); diff --git a/src/test/linters/lint.test.ts b/src/test/linters/lint.test.ts index bf96de28cfc3..c5d06918b801 100644 --- a/src/test/linters/lint.test.ts +++ b/src/test/linters/lint.test.ts @@ -1,11 +1,4 @@ -// -// Note: This example test is leveraging the Mocha test framework. -// Please refer to their documentation on https://mochajs.org/ for help. -// The module \'assert\' provides assertion methods from node import * as assert from 'assert'; - -// You can import and use all API from the \'vscode\' module -// as well as import your extension to test it import * as vscode from 'vscode'; import * as baseLinter from '../../client/linters/baseLinter'; import * as pyLint from '../../client/linters/pylint'; @@ -14,16 +7,16 @@ import * as flake8 from '../../client/linters/flake8'; import * as prospector from '../../client/linters/prospector'; import * as pydocstyle from '../../client/linters/pydocstyle'; import * as path from 'path'; -import * as settings from '../../client/common/configSettings'; import * as fs from 'fs-extra'; -import { initialize, IS_TRAVIS, closeActiveWindows } from '../initialize'; +import { rootWorkspaceUri, updateSetting, PythonSettingKeys } from '../common'; +import { PythonSettings } from '../../client/common/configSettings'; +import { initialize, IS_TRAVIS, closeActiveWindows, IS_MULTI_ROOT_TEST, initializeTest } from '../initialize'; import { execPythonFile } from '../../client/common/utils'; import { createDeferred } from '../../client/common/helpers'; import { Product, SettingToDisableProduct, Linters } from '../../client/common/installer'; import { EnumEx } from '../../client/common/enumUtils'; import { MockOutputChannel } from '../mockClasses'; -const pythonSettings = settings.PythonSettings.getInstance(); const pythoFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'linting'); const flake8ConfigPath = path.join(pythoFilesPath, 'flake8config'); const pep8ConfigPath = path.join(pythoFilesPath, 'pep8config'); @@ -127,35 +120,30 @@ suite('Linting', () => { suiteSetup(async () => { pylintFileToLintLines = fs.readFileSync(fileToLint).toString('utf-8').split(/\r?\n/g); await initialize(); - const version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); + const version = await execPythonFile(PythonSettings.getInstance().pythonPath, ['--version'], __dirname, true); isPython3Deferred.resolve(version.indexOf('3.') >= 0); }); - setup(() => { - pythonSettings.linting.lintOnSave = false; - pythonSettings.linting.lintOnTextChange = false; - pythonSettings.linting.enabled = true; - pythonSettings.linting.pylintEnabled = true; - pythonSettings.linting.flake8Enabled = true; - pythonSettings.linting.pep8Enabled = true; - pythonSettings.linting.prospectorEnabled = true; - pythonSettings.linting.pydocstyleEnabled = true; + setup(async () => { + await initializeTest(); + await resetSettings(); }); suiteTeardown(() => closeActiveWindows()); - teardown(() => { - closeActiveWindows(); - pythonSettings.linting.lintOnSave = false; - pythonSettings.linting.lintOnTextChange = false; - pythonSettings.linting.enabled = true; - pythonSettings.linting.pylintEnabled = true; - pythonSettings.linting.flake8Enabled = true; - pythonSettings.linting.pep8Enabled = true; - pythonSettings.linting.prospectorEnabled = true; - pythonSettings.linting.pydocstyleEnabled = true; + teardown(async () => { + await closeActiveWindows(); + await resetSettings(); }); - - async function testEnablingDisablingOfLinter(linter: baseLinter.BaseLinter, propertyName: string) { - pythonSettings.linting[propertyName] = true; - + async function resetSettings() { + await updateSetting('linting.lintOnSave', false, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + await updateSetting('linting.lintOnTextChange', false, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + await updateSetting('linting.enabled', true, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + await updateSetting('linting.pylintEnabled', true, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + await updateSetting('linting.flake8Enabled', true, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + await updateSetting('linting.pep8Enabled', true, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + await updateSetting('linting.prospectorEnabled', true, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + await updateSetting('linting.pydocstyleEnabled', true, rootWorkspaceUri, vscode.ConfigurationTarget.Workspace); + } + async function testEnablingDisablingOfLinter(linter: baseLinter.BaseLinter, setting: PythonSettingKeys) { + updateSetting(setting, true, rootWorkspaceUri, IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.Workspace : vscode.ConfigurationTarget.WorkspaceFolder) let cancelToken = new vscode.CancellationTokenSource(); disableAllButThisLinter(linter.product); const document = await vscode.workspace.openTextDocument(fileToLint); @@ -169,39 +157,40 @@ suite('Linting', () => { } test('Enable and Disable Pylint', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new pyLint.Linter(ch), 'pylintEnabled'); + testEnablingDisablingOfLinter(new pyLint.Linter(ch), 'linting.pylintEnabled'); }); test('Enable and Disable Pep8', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new pep8.Linter(ch), 'pep8Enabled'); + testEnablingDisablingOfLinter(new pep8.Linter(ch), 'linting.pep8Enabled'); }); test('Enable and Disable Flake8', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new flake8.Linter(ch), 'flake8Enabled'); + testEnablingDisablingOfLinter(new flake8.Linter(ch), 'linting.flake8Enabled'); }); test('Enable and Disable Prospector', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new prospector.Linter(ch), 'prospectorEnabled'); + testEnablingDisablingOfLinter(new prospector.Linter(ch), 'linting.prospectorEnabled'); }); test('Enable and Disable Pydocstyle', () => { let ch = new MockOutputChannel('Lint'); - testEnablingDisablingOfLinter(new pydocstyle.Linter(ch), 'pydocstyleEnabled'); + testEnablingDisablingOfLinter(new pydocstyle.Linter(ch), 'linting.pydocstyleEnabled'); }); - function disableAllButThisLinter(linterToEnable: Product) { - EnumEx.getNamesAndValues(Product).map(linter => { + async function disableAllButThisLinter(linterToEnable: Product) { + const promises = EnumEx.getNamesAndValues(Product).map(async linter => { if (Linters.indexOf(linter.value) === -1) { return; } - var setting = path.extname(SettingToDisableProduct.get(linter.value)).substring(1); - pythonSettings.linting[setting] = linterToEnable === linter.value; + var setting = SettingToDisableProduct.get(linter.value); + return updateSetting(setting as any, true, rootWorkspaceUri, IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.Workspace : vscode.ConfigurationTarget.WorkspaceFolder); }); + await Promise.all(promises); } function testLinterMessages(linter: baseLinter.BaseLinter, outputChannel: MockOutputChannel, pythonFile: string, messagesToBeReceived: baseLinter.ILintMessage[]): Thenable { let cancelToken = new vscode.CancellationTokenSource(); - disableAllButThisLinter(linter.product); - return vscode.workspace.openTextDocument(pythonFile) + return disableAllButThisLinter(linter.product) + .then(() => vscode.workspace.openTextDocument(pythonFile)) .then(document => vscode.window.showTextDocument(document)) .then(editor => { return linter.lint(editor.document, cancelToken.token); diff --git a/src/test/multiRootTest.ts b/src/test/multiRootTest.ts index b7ad307ef20c..4bbd5d7bb9b2 100644 --- a/src/test/multiRootTest.ts +++ b/src/test/multiRootTest.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -process.env.CODE_TESTS_WORKSPACE = path.join(__dirname, '..', '..', 'src', 'test', 'multiRootWkspc', 'multi.code-workspace'); +process.env.CODE_TESTS_WORKSPACE = path.join(__dirname, '..', '..', 'src', 'testMultiRootWkspc', 'multi.code-workspace'); function start() { require('../../node_modules/vscode/bin/test'); diff --git a/src/test/multiRootWkspc/parent/child/.vscode/settings.json b/src/test/multiRootWkspc/parent/child/.vscode/settings.json deleted file mode 100644 index b78380782cd9..000000000000 --- a/src/test/multiRootWkspc/parent/child/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "python.workspaceSymbols.enabled": true -} \ No newline at end of file diff --git a/src/test/multiRootWkspc/workspace2/workspace2.tags.file b/src/test/multiRootWkspc/workspace2/workspace2.tags.file deleted file mode 100644 index 2d54e7ed7c7b..000000000000 --- a/src/test/multiRootWkspc/workspace2/workspace2.tags.file +++ /dev/null @@ -1,24 +0,0 @@ -!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ -!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ -!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ -!_TAG_PROGRAM_AUTHOR Universal Ctags Team // -!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ -!_TAG_PROGRAM_URL https://ctags.io/ /official site/ -!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ -Foo C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^class Foo(object):$/;" kind:class line:5 -Workspace2Class C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^class Workspace2Class(object):$/;" kind:class line:5 -__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def __init__(self):$/;" kind:member line:8 -__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^ def __init__(self):$/;" kind:member line:8 -__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^__revision__ = None$/;" kind:variable line:3 -__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^__revision__ = None$/;" kind:variable line:3 -file.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py 1;" kind:file line:1 -meth1 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 -meth1OfWorkspace2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py /^ def meth1OfWorkspace2(self, arg):$/;" kind:member line:11 -meth2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 -meth3 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth3(self):$/;" kind:member line:21 -meth4 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth4(self):$/;" kind:member line:28 -meth5 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth5(self):$/;" kind:member line:38 -meth6 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth6(self):$/;" kind:member line:53 -meth7 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth7(self):$/;" kind:member line:68 -meth8 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\file.py /^ def meth8(self):$/;" kind:member line:80 -workspace2File.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace2\\workspace2File.py 1;" kind:file line:1 diff --git a/src/test/providers/shebangCodeLenseProvider.test.ts b/src/test/providers/shebangCodeLenseProvider.test.ts index edf66bf9fb02..59c7ecf2d5d6 100644 --- a/src/test/providers/shebangCodeLenseProvider.test.ts +++ b/src/test/providers/shebangCodeLenseProvider.test.ts @@ -3,9 +3,9 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as child_process from 'child_process'; import { IS_WINDOWS } from '../../client/common/configSettings'; +import { rootWorkspaceUri, updateSetting } from '../common'; import { ShebangCodeLensProvider } from '../../client/providers/shebangCodeLensProvider'; - -import { initialize, IS_TRAVIS, closeActiveWindows } from '../initialize'; +import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; import { getFirstNonEmptyLineFromMultilineString } from '../../client/interpreter/helpers'; const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'shebang'); @@ -14,19 +14,17 @@ const fileShebangEnv = path.join(autoCompPath, 'shebangEnv.py'); const fileShebangInvalid = path.join(autoCompPath, 'shebangInvalid.py'); const filePlain = path.join(autoCompPath, 'plain.py'); -var settings = vscode.workspace.getConfiguration('python'); -const origPythonPath = settings.get('pythonPath'); - suite('Shebang detection', () => { suiteSetup(() => initialize()); - suiteTeardown(() => vscode.workspace.getConfiguration('python').update('pythonPath', origPythonPath)); - teardown(async () => { + suiteTeardown(async () => { + await initialize(); await closeActiveWindows(); - await vscode.workspace.getConfiguration('python').update('pythonPath', origPythonPath); }); + setup(() => initializeTest()); test('Shebang available, CodeLens showing', async () => { - await settings.update('pythonPath', 'someUnknownInterpreter'); + const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; + await updateSetting('pythonPath', 'someUnknownInterpreter', rootWorkspaceUri, configTarget); const editor = await openFile(fileShebang); const codeLenses = await setupCodeLens(editor); @@ -34,12 +32,12 @@ suite('Shebang detection', () => { let codeLens = codeLenses[0]; assert(codeLens.range.isSingleLine, 'Invalid CodeLens Range'); assert.equal(codeLens.command.command, 'python.setShebangInterpreter'); - }); test('Shebang available, CodeLens hiding', async () => { const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - await settings.update('pythonPath', pythonPath); + const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, configTarget); const editor = await openFile(fileShebang); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); @@ -48,7 +46,8 @@ suite('Shebang detection', () => { test('Shebang not available (invalid shebang)', async () => { const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - await settings.update('pythonPath', pythonPath); + const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, configTarget); const editor = await openFile(fileShebangInvalid); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although shebang is invalid'); @@ -56,7 +55,8 @@ suite('Shebang detection', () => { if (!IS_WINDOWS) { test('Shebang available, CodeLens showing with env', async () => { - await settings.update('pythonPath', 'p1'); + const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; + await updateSetting('pythonPath', 'p1', rootWorkspaceUri, configTarget); const editor = await openFile(fileShebangEnv); const codeLenses = await setupCodeLens(editor); @@ -69,7 +69,8 @@ suite('Shebang detection', () => { test('Shebang available, CodeLens hiding with env', async () => { const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - await settings.update('pythonPath', pythonPath); + const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, configTarget); const editor = await openFile(fileShebangEnv); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); diff --git a/src/test/refactor/extension.refactor.extract.method.test.ts b/src/test/refactor/extension.refactor.extract.method.test.ts index 021bb90532ff..436c1f8f519f 100644 --- a/src/test/refactor/extension.refactor.extract.method.test.ts +++ b/src/test/refactor/extension.refactor.extract.method.test.ts @@ -4,9 +4,9 @@ import * as assert from 'assert'; // as well as import your extension to test it import * as vscode from 'vscode'; import * as path from 'path'; -import * as settings from '../../client/common/configSettings'; import * as fs from 'fs-extra'; -import { initialize, closeActiveWindows, IS_TRAVIS, wait } from './../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { initialize, closeActiveWindows, IS_TRAVIS, wait, initializeTest } from './../initialize'; import { Position } from 'vscode'; import { extractMethod } from '../../client/providers/simpleRefactorProvider'; import { RefactorProxy } from '../../client/refactor/proxy'; @@ -14,7 +14,6 @@ import { getTextEditsFromPatch } from '../../client/common/editor'; import { MockOutputChannel } from './../mockClasses'; const EXTENSION_DIR = path.join(__dirname, '..', '..', '..'); -const pythonSettings = settings.PythonSettings.getInstance(); const refactorSourceFile = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'refactoring', 'standAlone', 'refactor.py'); const refactorTargetFile = path.join(__dirname, '..', '..', '..', 'out', 'test', 'pythonFiles', 'refactoring', 'standAlone', 'refactor.py'); @@ -41,7 +40,7 @@ suite('Method Extraction', () => { fs.unlinkSync(refactorTargetFile); } fs.copySync(refactorSourceFile, refactorTargetFile, { overwrite: true }); - await closeActiveWindows(); + await initializeTest(); (vscode).commands.executeCommand = (cmd) => Promise.resolve(); }); teardown(() => { @@ -49,7 +48,8 @@ suite('Method Extraction', () => { return closeActiveWindows(); }); - function testingMethodExtraction(shouldError: boolean, pythonSettings: settings.IPythonSettings, startPos: Position, endPos: Position) { + function testingMethodExtraction(shouldError: boolean, startPos: Position, endPos: Position) { + const pythonSettings = PythonSettings.getInstance(vscode.Uri.file(refactorTargetFile)); let rangeOfTextToExtract = new vscode.Range(startPos, endPos); let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, path.dirname(refactorTargetFile)); let expectedTextEdits: vscode.TextEdit[]; @@ -61,7 +61,7 @@ suite('Method Extraction', () => { mockTextDoc = textDocument; expectedTextEdits = getTextEditsFromPatch(textDocument.getText(), DIFF); resolve(); - }, error => reject(error)) + }, error => reject(error)); }) .then(() => proxy.extractMethod(mockTextDoc, 'myNewMethod', refactorTargetFile, rangeOfTextToExtract, options)) .then(response => { @@ -93,16 +93,16 @@ suite('Method Extraction', () => { test('Extract Method', done => { let startPos = new vscode.Position(239, 0); let endPos = new vscode.Position(241, 35); - testingMethodExtraction(false, pythonSettings, startPos, endPos).then(() => done(), done); + testingMethodExtraction(false, startPos, endPos).then(() => done(), done); }); test('Extract Method will fail if complete statements are not selected', done => { let startPos = new vscode.Position(239, 30); let endPos = new vscode.Position(241, 35); - testingMethodExtraction(true, pythonSettings, startPos, endPos).then(() => done(), done); + testingMethodExtraction(true, startPos, endPos).then(() => done(), done); }); - function testingMethodExtractionEndToEnd(shouldError: boolean, pythonSettings: settings.IPythonSettings, startPos: Position, endPos: Position) { + function testingMethodExtractionEndToEnd(shouldError: boolean, startPos: Position, endPos: Position) { let ch = new MockOutputChannel('Python'); let textDocument: vscode.TextDocument; let textEditor: vscode.TextEditor; @@ -161,13 +161,13 @@ suite('Method Extraction', () => { test('Extract Method (end to end)', done => { let startPos = new vscode.Position(239, 0); let endPos = new vscode.Position(241, 35); - testingMethodExtractionEndToEnd(false, pythonSettings, startPos, endPos).then(() => done(), done); + testingMethodExtractionEndToEnd(false, startPos, endPos).then(() => done(), done); }); } test('Extract Method will fail if complete statements are not selected', done => { let startPos = new vscode.Position(239, 30); let endPos = new vscode.Position(241, 35); - testingMethodExtractionEndToEnd(true, pythonSettings, startPos, endPos).then(() => done(), done); + testingMethodExtractionEndToEnd(true, startPos, endPos).then(() => done(), done); }); }); diff --git a/src/test/refactor/extension.refactor.extract.var.test.ts b/src/test/refactor/extension.refactor.extract.var.test.ts index 1e2a896a4e56..06144c333e18 100644 --- a/src/test/refactor/extension.refactor.extract.var.test.ts +++ b/src/test/refactor/extension.refactor.extract.var.test.ts @@ -4,9 +4,9 @@ import * as assert from 'assert'; // as well as import your extension to test it import * as vscode from 'vscode'; import * as path from 'path'; -import * as settings from '../../client/common/configSettings'; import * as fs from 'fs-extra'; -import { initialize, closeActiveWindows, IS_TRAVIS, wait } from './../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { closeActiveWindows, initialize, initializeTest, IS_TRAVIS, wait } from './../initialize'; import { Position } from 'vscode'; import { extractVariable } from '../../client/providers/simpleRefactorProvider'; import { RefactorProxy } from '../../client/refactor/proxy'; @@ -14,7 +14,6 @@ import { getTextEditsFromPatch } from '../../client/common/editor'; import { MockOutputChannel } from './../mockClasses'; const EXTENSION_DIR = path.join(__dirname, '..', '..', '..'); -const pythonSettings = settings.PythonSettings.getInstance(); const refactorSourceFile = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'refactoring', 'standAlone', 'refactor.py'); const refactorTargetFile = path.join(__dirname, '..', '..', '..', 'out', 'test', 'pythonFiles', 'refactoring', 'standAlone', 'refactor.py'); @@ -26,9 +25,9 @@ suite('Variable Extraction', () => { // Hack hac hack const oldExecuteCommand = vscode.commands.executeCommand; const options: vscode.TextEditorOptions = { cursorStyle: vscode.TextEditorCursorStyle.Line, insertSpaces: true, lineNumbers: vscode.TextEditorLineNumbersStyle.Off, tabSize: 4 }; - suiteSetup(done => { + suiteSetup(async () => { fs.copySync(refactorSourceFile, refactorTargetFile, { overwrite: true }); - initialize().then(() => done(), () => done()); + await initialize(); }); suiteTeardown(() => { vscode.commands.executeCommand = oldExecuteCommand; @@ -40,7 +39,7 @@ suite('Variable Extraction', () => { fs.unlinkSync(refactorTargetFile); } fs.copySync(refactorSourceFile, refactorTargetFile, { overwrite: true }); - await closeActiveWindows(); + await initializeTest(); (vscode).commands.executeCommand = (cmd) => Promise.resolve(); }); teardown(() => { @@ -48,7 +47,8 @@ suite('Variable Extraction', () => { return closeActiveWindows(); }); - function testingVariableExtraction(shouldError: boolean, pythonSettings: settings.IPythonSettings, startPos: Position, endPos: Position) { + function testingVariableExtraction(shouldError: boolean, startPos: Position, endPos: Position) { + const pythonSettings = PythonSettings.getInstance(vscode.Uri.file(refactorTargetFile)); let rangeOfTextToExtract = new vscode.Range(startPos, endPos); let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, path.dirname(refactorTargetFile)); let expectedTextEdits: vscode.TextEdit[]; @@ -92,16 +92,16 @@ suite('Variable Extraction', () => { test('Extract Variable', done => { let startPos = new vscode.Position(234, 29); let endPos = new vscode.Position(234, 38); - testingVariableExtraction(false, pythonSettings, startPos, endPos).then(() => done(), done); + testingVariableExtraction(false, startPos, endPos).then(() => done(), done); }); test('Extract Variable fails if whole string not selected', done => { let startPos = new vscode.Position(234, 20); let endPos = new vscode.Position(234, 38); - testingVariableExtraction(true, pythonSettings, startPos, endPos).then(() => done(), done); + testingVariableExtraction(true, startPos, endPos).then(() => done(), done); }); - function testingVariableExtractionEndToEnd(shouldError: boolean, pythonSettings: settings.IPythonSettings, startPos: Position, endPos: Position) { + function testingVariableExtractionEndToEnd(shouldError: boolean, startPos: Position, endPos: Position) { let ch = new MockOutputChannel('Python'); let textDocument: vscode.TextDocument; let textEditor: vscode.TextEditor; @@ -160,13 +160,13 @@ suite('Variable Extraction', () => { test('Extract Variable (end to end)', done => { let startPos = new vscode.Position(234, 29); let endPos = new vscode.Position(234, 38); - testingVariableExtractionEndToEnd(false, pythonSettings, startPos, endPos).then(() => done(), done); + testingVariableExtractionEndToEnd(false, startPos, endPos).then(() => done(), done); }); } test('Extract Variable fails if whole string not selected (end to end)', done => { let startPos = new vscode.Position(234, 20); let endPos = new vscode.Position(234, 38); - testingVariableExtractionEndToEnd(true, pythonSettings, startPos, endPos).then(() => done(), done); + testingVariableExtractionEndToEnd(true, startPos, endPos).then(() => done(), done); }); }); diff --git a/src/test/unittests/nosetest.test.ts b/src/test/unittests/nosetest.test.ts index 26f4bd39a0ea..870ae5639aa1 100644 --- a/src/test/unittests/nosetest.test.ts +++ b/src/test/unittests/nosetest.test.ts @@ -2,28 +2,28 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; -import * as configSettings from '../../client/common/configSettings'; import * as nose from '../../client/unittests/nosetest/main'; -import { initialize } from './../initialize'; +import { rootWorkspaceUri, updateSetting } from '../common'; +import { initialize, initializeTest, IS_MULTI_ROOT_TEST } from './../initialize'; import { TestsToRun } from '../../client/unittests/common/contracts'; import { TestResultDisplay } from '../../client/unittests/display/main'; import { MockOutputChannel } from './../mockClasses'; -const pythonSettings = configSettings.PythonSettings.getInstance(); const UNITTEST_TEST_FILES_PATH = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'standard'); const UNITTEST_SINGLE_TEST_FILE_PATH = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'single'); const filesToDelete = [path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'standard', '.noseids'), path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'cwd', 'src', '.noseids')]; const unitTestTestFilesCwdPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'cwd', 'src'); -const originalArgs = pythonSettings.unitTest.nosetestArgs; suite('Unit Tests (nosetest)', () => { + const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; suiteSetup(async () => { filesToDelete.forEach(file => { if (fs.existsSync(file)) { fs.unlinkSync(file); } }); + await updateSetting('unitTest.nosetestArgs', [], rootWorkspaceUri, configTarget); await initialize(); }); suiteTeardown(() => { @@ -33,15 +33,16 @@ suite('Unit Tests (nosetest)', () => { } }); }); - setup(() => { - pythonSettings.unitTest.nosetestArgs = originalArgs; + setup(async () => { outChannel = new MockOutputChannel('Python Test Log'); testResultDisplay = new TestResultDisplay(outChannel); + await initializeTest(); }); teardown(() => { outChannel.dispose(); testManager.dispose(); testResultDisplay.dispose(); + return updateSetting('unitTest.nosetestArgs', [], rootWorkspaceUri, configTarget); }); function createTestManager(rootDir: string = rootDirectory) { testManager = new nose.TestManager(rootDir, outChannel); @@ -52,7 +53,6 @@ suite('Unit Tests (nosetest)', () => { let outChannel: vscode.OutputChannel; test('Discover Tests (single test file)', async () => { - pythonSettings.unitTest.nosetestArgs = []; testManager = new nose.TestManager(UNITTEST_SINGLE_TEST_FILE_PATH, outChannel); const tests = await testManager.discoverTests(true, true) assert.equal(tests.testFiles.length, 2, 'Incorrect number of test files'); @@ -62,7 +62,6 @@ suite('Unit Tests (nosetest)', () => { }); test('Check that nameToRun in testSuits has class name after : (single test file)', async () => { - pythonSettings.unitTest.nosetestArgs = []; testManager = new nose.TestManager(UNITTEST_SINGLE_TEST_FILE_PATH, outChannel); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 2, 'Incorrect number of test files'); @@ -72,7 +71,6 @@ suite('Unit Tests (nosetest)', () => { }); test('Discover Tests (pattern = test_)', async () => { - pythonSettings.unitTest.nosetestArgs = []; createTestManager(); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 6, 'Incorrect number of test files'); @@ -87,9 +85,7 @@ suite('Unit Tests (nosetest)', () => { }); test('Discover Tests (pattern = _test_)', async () => { - pythonSettings.unitTest.nosetestArgs = [ - '-m=*test*' - ]; + await updateSetting('unitTest.nosetestArgs', ['-m=*test*'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 6, 'Incorrect number of test files'); @@ -104,7 +100,6 @@ suite('Unit Tests (nosetest)', () => { }); test('Run Tests', async () => { - pythonSettings.unitTest.nosetestArgs = []; createTestManager(); const results = await testManager.runTest(); assert.equal(results.summary.errors, 5, 'Errors'); @@ -114,7 +109,6 @@ suite('Unit Tests (nosetest)', () => { }); test('Run Failed Tests', async () => { - pythonSettings.unitTest.nosetestArgs = []; createTestManager(); let results = await testManager.runTest(); assert.equal(results.summary.errors, 5, 'Errors'); @@ -130,7 +124,6 @@ suite('Unit Tests (nosetest)', () => { }); test('Run Specific Test File', async () => { - pythonSettings.unitTest.nosetestArgs = []; createTestManager(); const tests = await testManager.discoverTests(true, true); const testFile: TestsToRun = { testFile: [tests.testFiles[0]], testFolder: [], testFunction: [], testSuite: [] }; @@ -142,7 +135,6 @@ suite('Unit Tests (nosetest)', () => { }); test('Run Specific Test Suite', async () => { - pythonSettings.unitTest.nosetestArgs = []; createTestManager(); const tests = await testManager.discoverTests(true, true); const testSuite: TestsToRun = { testFile: [], testFolder: [], testFunction: [], testSuite: [tests.testSuits[0].testSuite] }; @@ -154,7 +146,6 @@ suite('Unit Tests (nosetest)', () => { }); test('Run Specific Test Function', async () => { - pythonSettings.unitTest.nosetestArgs = []; createTestManager(); const tests = await testManager.discoverTests(true, true); const testFn: TestsToRun = { testFile: [], testFolder: [], testFunction: [tests.testFunctions[0].testFunction], testSuite: [] }; @@ -166,7 +157,7 @@ suite('Unit Tests (nosetest)', () => { }); test('Setting cwd should return tests', async () => { - pythonSettings.unitTest.nosetestArgs = ['tests']; + await updateSetting('unitTest.nosetestArgs', ['tests'], rootWorkspaceUri, configTarget); createTestManager(unitTestTestFilesCwdPath); const tests = await testManager.discoverTests(true, true); diff --git a/src/test/unittests/pytest.test.ts b/src/test/unittests/pytest.test.ts index 4d7dcdcbc664..7d5dd23fc6e4 100644 --- a/src/test/unittests/pytest.test.ts +++ b/src/test/unittests/pytest.test.ts @@ -3,10 +3,11 @@ import * as vscode from 'vscode'; import * as pytest from '../../client/unittests/pytest/main'; import * as path from 'path'; import * as configSettings from '../../client/common/configSettings'; -import { initialize } from './../initialize'; +import { initialize, initializeTest, IS_MULTI_ROOT_TEST } from './../initialize'; import { TestsToRun, TestFile } from '../../client/unittests/common/contracts'; import { TestResultDisplay } from '../../client/unittests/display/main'; import { MockOutputChannel } from './../mockClasses'; +import { rootWorkspaceUri, updateSetting } from '../common'; const pythonSettings = configSettings.PythonSettings.getInstance(); const UNITTEST_TEST_FILES_PATH = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'standard'); @@ -15,16 +16,22 @@ const UNITTEST_TEST_FILES_PATH_WITH_CONFIGS = path.join(__dirname, '..', '..', ' const unitTestTestFilesCwdPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'cwd', 'src'); suite('Unit Tests (PyTest)', () => { - suiteSetup(() => initialize()); + const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; + suiteSetup(async () => { + await initialize(); + await updateSetting('unitTest.pyTestArgs', [], rootWorkspaceUri, configTarget); + }); setup(() => { rootDirectory = UNITTEST_TEST_FILES_PATH; outChannel = new MockOutputChannel('Python Test Log'); testResultDisplay = new TestResultDisplay(outChannel); + return initializeTest(); }); teardown(() => { outChannel.dispose(); testManager.dispose(); testResultDisplay.dispose(); + return updateSetting('unitTest.pyTestArgs', [], rootWorkspaceUri, configTarget); }); function createTestManager(rootDir: string = rootDirectory) { testManager = new pytest.TestManager(rootDir, outChannel); @@ -35,8 +42,6 @@ suite('Unit Tests (PyTest)', () => { let outChannel: vscode.OutputChannel; test('Discover Tests (single test file)', async () => { - pythonSettings.unitTest.nosetestArgs = [ - ]; testManager = new pytest.TestManager(UNITTEST_SINGLE_TEST_FILE_PATH, outChannel); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 2, 'Incorrect number of test files'); @@ -47,9 +52,7 @@ suite('Unit Tests (PyTest)', () => { }); test('Discover Tests (pattern = test_)', async () => { - pythonSettings.unitTest.pyTestArgs = [ - '-k=test_' - ]; + await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 6, 'Incorrect number of test files'); @@ -64,9 +67,7 @@ suite('Unit Tests (PyTest)', () => { }); test('Discover Tests (pattern = _test)', async () => { - pythonSettings.unitTest.pyTestArgs = [ - '-k=_test.py' - ]; + await updateSetting('unitTest.pyTestArgs', ['-k=_test.py'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 1, 'Incorrect number of test files'); @@ -89,9 +90,7 @@ suite('Unit Tests (PyTest)', () => { }); test('Run Tests', async () => { - pythonSettings.unitTest.pyTestArgs = [ - '-k=test_' - ]; + await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); createTestManager(); const results = await testManager.runTest(); assert.equal(results.summary.errors, 0, 'Errors'); @@ -101,11 +100,9 @@ suite('Unit Tests (PyTest)', () => { }); test('Run Failed Tests', async () => { - pythonSettings.unitTest.pyTestArgs = [ - '-k=test_' - ]; + await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); createTestManager(); - let results = await testManager.runTest() + let results = await testManager.runTest(); assert.equal(results.summary.errors, 0, 'Errors'); assert.equal(results.summary.failures, 9, 'Failures'); assert.equal(results.summary.passed, 17, 'Passed'); @@ -119,9 +116,7 @@ suite('Unit Tests (PyTest)', () => { }); test('Run Specific Test File', async () => { - pythonSettings.unitTest.pyTestArgs = [ - '-k=test_' - ]; + await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); createTestManager(); await testManager.discoverTests(true, true); const testFile: TestFile = { @@ -142,13 +137,11 @@ suite('Unit Tests (PyTest)', () => { }); test('Run Specific Test Suite', async () => { - pythonSettings.unitTest.pyTestArgs = [ - '-k=test_' - ]; + await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); const testSuite: TestsToRun = { testFile: [], testFolder: [], testFunction: [], testSuite: [tests.testSuits[0].testSuite] }; - const results = await testManager.runTest(testSuite) + const results = await testManager.runTest(testSuite); assert.equal(results.summary.errors, 0, 'Errors'); assert.equal(results.summary.failures, 1, 'Failures'); assert.equal(results.summary.passed, 1, 'Passed'); @@ -156,9 +149,7 @@ suite('Unit Tests (PyTest)', () => { }); test('Run Specific Test Function', async () => { - pythonSettings.unitTest.pyTestArgs = [ - '-k=test_' - ]; + await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); const testFn: TestsToRun = { testFile: [], testFolder: [], testFunction: [tests.testFunctions[0].testFunction], testSuite: [] }; @@ -171,10 +162,7 @@ suite('Unit Tests (PyTest)', () => { test('Setting cwd should return tests', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test*.py' - ]; + await updateSetting('unitTest.pyTestArgs', ['-s=./tests', '-p=test*.py'], rootWorkspaceUri, configTarget); createTestManager(unitTestTestFilesCwdPath); const tests = await testManager.discoverTests(true, true); diff --git a/src/test/unittests/unittest.test.ts b/src/test/unittests/unittest.test.ts index 43d4fdab710f..b443d7a260b4 100644 --- a/src/test/unittests/unittest.test.ts +++ b/src/test/unittests/unittest.test.ts @@ -2,23 +2,36 @@ import * as assert from 'assert'; import * as path from 'path'; import * as configSettings from '../../client/common/configSettings'; import * as unittest from '../../client/unittests/unittest/main'; -import { initialize } from './../initialize'; +import { initialize, initializeTest, IS_MULTI_ROOT_TEST } from './../initialize'; import { TestsToRun } from '../../client/unittests/common/contracts'; import { TestResultDisplay } from '../../client/unittests/display/main'; import { MockOutputChannel } from './../mockClasses'; +import { ConfigurationTarget } from 'vscode'; +import { rootWorkspaceUri, updateSetting } from '../common'; -const pythonSettings = configSettings.PythonSettings.getInstance(); const testFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles'); const UNITTEST_TEST_FILES_PATH = path.join(testFilesPath, 'standard'); const UNITTEST_SINGLE_TEST_FILE_PATH = path.join(testFilesPath, 'single'); const unitTestTestFilesCwdPath = path.join(testFilesPath, 'cwd', 'src'); const unitTestSpecificTestFilesPath = path.join(testFilesPath, 'specificTest'); +const defaultUnitTestArgs = [ + "-v", + "-s", + ".", + "-p", + "*test*.py" +]; suite('Unit Tests (unittest)', () => { - suiteSetup(() => initialize()); + const configTarget = IS_MULTI_ROOT_TEST ? ConfigurationTarget.WorkspaceFolder : ConfigurationTarget.Workspace; + suiteSetup(async () => { + await initialize(); + await updateSetting('unitTest.unittestArgs', defaultUnitTestArgs, rootWorkspaceUri, configTarget); + }); setup(() => { outChannel = new MockOutputChannel('Python Test Log'); testResultDisplay = new TestResultDisplay(outChannel); + return initializeTest(); }); teardown(() => { outChannel.dispose(); @@ -34,10 +47,7 @@ suite('Unit Tests (unittest)', () => { let outChannel: MockOutputChannel; test('Discover Tests (single test file)', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test_*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_*.py'], rootWorkspaceUri, configTarget); testManager = new unittest.TestManager(UNITTEST_SINGLE_TEST_FILE_PATH, outChannel); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 1, 'Incorrect number of test files'); @@ -47,10 +57,7 @@ suite('Unit Tests (unittest)', () => { }); test('Discover Tests', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test_*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_*.py'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 2, 'Incorrect number of test files'); @@ -61,10 +68,7 @@ suite('Unit Tests (unittest)', () => { }); test('Discover Tests (pattern = *_test_*.py)', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=*_test*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=*_test*.py'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); assert.equal(tests.testFiles.length, 1, 'Incorrect number of test files'); @@ -74,10 +78,7 @@ suite('Unit Tests (unittest)', () => { }); test('Run Tests', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-v', '-s', './tests', - '-p', 'test_unittest*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-v', '-s', './tests', '-p', 'test_unittest*.py'], rootWorkspaceUri, configTarget); createTestManager(); const results = await testManager.runTest(); assert.equal(results.summary.errors, 1, 'Errors'); @@ -103,10 +104,7 @@ suite('Unit Tests (unittest)', () => { // }); test('Run Failed Tests', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test_unittest*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); createTestManager(); let results = await testManager.runTest(); assert.equal(results.summary.errors, 1, 'Errors'); @@ -122,10 +120,7 @@ suite('Unit Tests (unittest)', () => { }); test('Run Specific Test File', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test_unittest*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); createTestManager(unitTestSpecificTestFilesPath); const tests = await testManager.discoverTests(true, true); @@ -140,10 +135,7 @@ suite('Unit Tests (unittest)', () => { }); test('Run Specific Test Suite', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test_unittest*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); createTestManager(unitTestSpecificTestFilesPath); const tests = await testManager.discoverTests(true, true); @@ -158,10 +150,7 @@ suite('Unit Tests (unittest)', () => { }); test('Run Specific Test Function', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test_unittest*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_unittest*.py'], rootWorkspaceUri, configTarget); createTestManager(); const tests = await testManager.discoverTests(true, true); const testFn: TestsToRun = { testFile: [], testFolder: [], testFunction: [tests.testFunctions[0].testFunction], testSuite: [] }; @@ -173,10 +162,7 @@ suite('Unit Tests (unittest)', () => { }); test('Setting cwd should return tests', async () => { - pythonSettings.unitTest.unittestArgs = [ - '-s=./tests', - '-p=test_*.py' - ]; + await updateSetting('unitTest.unittestArgs', ['-s=./tests', '-p=test_*.py'], rootWorkspaceUri, configTarget); createTestManager(unitTestTestFilesCwdPath); const tests = await testManager.discoverTests(true, true); diff --git a/src/test/workspaceSymbols/common.ts b/src/test/workspaceSymbols/common.ts deleted file mode 100644 index f713b64969c3..000000000000 --- a/src/test/workspaceSymbols/common.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ConfigurationTarget, Uri, workspace } from 'vscode'; -import { PythonSettings } from '../../client/common/configSettings'; - -export async function enableDisableWorkspaceSymbols(resource: Uri, enabled: boolean, configTarget: ConfigurationTarget) { - let settings = workspace.getConfiguration('python', resource); - await settings.update('workspaceSymbols.enabled', enabled, configTarget); - PythonSettings.dispose(); -} \ No newline at end of file diff --git a/src/test/workspaceSymbols/multiroot.test.ts b/src/test/workspaceSymbols/multiroot.test.ts index 270920898526..bbd352f11d6f 100644 --- a/src/test/workspaceSymbols/multiroot.test.ts +++ b/src/test/workspaceSymbols/multiroot.test.ts @@ -1,30 +1,29 @@ import * as assert from 'assert'; import * as path from 'path'; import { CancellationTokenSource, ConfigurationTarget, Uri } from 'vscode'; -import { initialize, closeActiveWindows } from '../initialize'; +import { closeActiveWindows, initialize, initializeTest } from '../initialize'; import { Generator } from '../../client/workspaceSymbols/generator'; import { MockOutputChannel } from '../mockClasses'; import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; -import { enableDisableWorkspaceSymbols } from './common'; -import { PythonSettings } from '../../client/common/configSettings'; +import { updateSetting } from './../common'; -const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'multiRootWkspc'); +const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'testMultiRootWkspc'); suite('Multiroot Workspace Symbols', () => { suiteSetup(() => initialize()); - setup(() => PythonSettings.dispose()); + setup(() => initializeTest()); suiteTeardown(() => closeActiveWindows()); teardown(async () => { await closeActiveWindows(); - await enableDisableWorkspaceSymbols(Uri.file(path.join(multirootPath, 'parent', 'child')), false, ConfigurationTarget.WorkspaceFolder); - await enableDisableWorkspaceSymbols(Uri.file(path.join(multirootPath, 'workspace2')), false, ConfigurationTarget.WorkspaceFolder); + await updateSetting('workspaceSymbols.enabled', false, Uri.file(path.join(multirootPath, 'parent', 'child')), ConfigurationTarget.WorkspaceFolder); + await updateSetting('workspaceSymbols.enabled', false, Uri.file(path.join(multirootPath, 'workspace2')), ConfigurationTarget.WorkspaceFolder); }); test(`symbols should be returned when enabeld and vice versa`, async () => { const childWorkspaceUri = Uri.file(path.join(multirootPath, 'parent', 'child')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(childWorkspaceUri, false, ConfigurationTarget.WorkspaceFolder); + await updateSetting('workspaceSymbols.enabled', false, childWorkspaceUri, ConfigurationTarget.WorkspaceFolder); let generator = new Generator(childWorkspaceUri, outputChannel); let provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -32,7 +31,7 @@ suite('Multiroot Workspace Symbols', () => { assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); generator.dispose(); - await enableDisableWorkspaceSymbols(childWorkspaceUri, true, ConfigurationTarget.WorkspaceFolder); + await updateSetting('workspaceSymbols.enabled', true, childWorkspaceUri, ConfigurationTarget.WorkspaceFolder); generator = new Generator(childWorkspaceUri, outputChannel); provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -44,8 +43,8 @@ suite('Multiroot Workspace Symbols', () => { const workspace2Uri = Uri.file(path.join(multirootPath, 'workspace2')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(childWorkspaceUri, true, ConfigurationTarget.WorkspaceFolder); - await enableDisableWorkspaceSymbols(workspace2Uri, true, ConfigurationTarget.WorkspaceFolder); + await updateSetting('workspaceSymbols.enabled', true, childWorkspaceUri, ConfigurationTarget.WorkspaceFolder); + await updateSetting('workspaceSymbols.enabled', true, workspace2Uri, ConfigurationTarget.WorkspaceFolder); const generators = [ new Generator(childWorkspaceUri, outputChannel), diff --git a/src/test/workspaceSymbols/standard.test.ts b/src/test/workspaceSymbols/standard.test.ts index f7e0270d4fd9..c170f15ef964 100644 --- a/src/test/workspaceSymbols/standard.test.ts +++ b/src/test/workspaceSymbols/standard.test.ts @@ -1,11 +1,11 @@ import * as assert from 'assert'; import * as path from 'path'; import { CancellationTokenSource, ConfigurationTarget, Uri } from 'vscode'; -import { initialize, closeActiveWindows } from '../initialize'; +import { closeActiveWindows, initialize, initializeTest } from '../initialize'; import { Generator } from '../../client/workspaceSymbols/generator'; import { MockOutputChannel } from '../mockClasses'; import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider'; -import { enableDisableWorkspaceSymbols } from './common'; +import { updateSetting } from './../common'; import { PythonSettings } from '../../client/common/configSettings'; const symbolFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'symbolFiles'); @@ -13,17 +13,17 @@ const symbolFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 's suite('Workspace Symbols', () => { suiteSetup(() => initialize()); suiteTeardown(() => closeActiveWindows()); - setup(() => PythonSettings.dispose()); + setup(() => initializeTest()); teardown(async () => { await closeActiveWindows(); - await enableDisableWorkspaceSymbols(Uri.file(path.join(symbolFilesPath, 'file.py')), false, ConfigurationTarget.Workspace); + await updateSetting('workspaceSymbols.enabled', false, Uri.file(path.join(symbolFilesPath, 'file.py')), ConfigurationTarget.Workspace); }); test(`symbols should be returned when enabeld and vice versa`, async () => { const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(workspaceUri, false, ConfigurationTarget.Workspace); + await updateSetting('workspaceSymbols.enabled', false, workspaceUri, ConfigurationTarget.Workspace); let generator = new Generator(workspaceUri, outputChannel); let provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -31,7 +31,7 @@ suite('Workspace Symbols', () => { assert.equal(symbols.length, 0, 'Symbols returned even when workspace symbols are turned off'); generator.dispose(); - await enableDisableWorkspaceSymbols(workspaceUri, true, ConfigurationTarget.Workspace); + await updateSetting('workspaceSymbols.enabled', true, workspaceUri, ConfigurationTarget.Workspace); generator = new Generator(workspaceUri, outputChannel); provider = new WorkspaceSymbolProvider([generator], outputChannel); @@ -42,7 +42,7 @@ suite('Workspace Symbols', () => { const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(workspaceUri, true, ConfigurationTarget.Workspace); + await updateSetting('workspaceSymbols.enabled', true, workspaceUri, ConfigurationTarget.Workspace); const generators = [new Generator(workspaceUri, outputChannel)]; const provider = new WorkspaceSymbolProvider(generators, outputChannel); diff --git a/src/test/multiRootWkspc/disableLinters/.vscode/tags b/src/testMultiRootWkspc/disableLinters/.vscode/tags similarity index 100% rename from src/test/multiRootWkspc/disableLinters/.vscode/tags rename to src/testMultiRootWkspc/disableLinters/.vscode/tags diff --git a/src/test/multiRootWkspc/disableLinters/file.py b/src/testMultiRootWkspc/disableLinters/file.py similarity index 100% rename from src/test/multiRootWkspc/disableLinters/file.py rename to src/testMultiRootWkspc/disableLinters/file.py diff --git a/src/test/multiRootWkspc/multi.code-workspace b/src/testMultiRootWkspc/multi.code-workspace similarity index 82% rename from src/test/multiRootWkspc/multi.code-workspace rename to src/testMultiRootWkspc/multi.code-workspace index 3c2ef8e8ca44..891cdf0bd28d 100644 --- a/src/test/multiRootWkspc/multi.code-workspace +++ b/src/testMultiRootWkspc/multi.code-workspace @@ -14,6 +14,9 @@ }, { "path": "disableLinters" + }, + { + "path": "../test" } ], "settings": { @@ -24,6 +27,7 @@ "python.linting.pylintEnabled": false, "python.linting.pep8Enabled": true, "python.linting.prospectorEnabled": true, - "python.workspaceSymbols.enabled": true + "python.workspaceSymbols.enabled": true, + "python.pythonPath": "python" } } diff --git a/src/testMultiRootWkspc/parent/child/.vscode/settings.json b/src/testMultiRootWkspc/parent/child/.vscode/settings.json new file mode 100644 index 000000000000..c404e94945a9 --- /dev/null +++ b/src/testMultiRootWkspc/parent/child/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.workspaceSymbols.enabled": false +} \ No newline at end of file diff --git a/src/test/multiRootWkspc/parent/child/.vscode/tags b/src/testMultiRootWkspc/parent/child/.vscode/tags similarity index 100% rename from src/test/multiRootWkspc/parent/child/.vscode/tags rename to src/testMultiRootWkspc/parent/child/.vscode/tags diff --git a/src/test/multiRootWkspc/parent/child/childFile.py b/src/testMultiRootWkspc/parent/child/childFile.py similarity index 100% rename from src/test/multiRootWkspc/parent/child/childFile.py rename to src/testMultiRootWkspc/parent/child/childFile.py diff --git a/src/test/multiRootWkspc/parent/child/file.py b/src/testMultiRootWkspc/parent/child/file.py similarity index 100% rename from src/test/multiRootWkspc/parent/child/file.py rename to src/testMultiRootWkspc/parent/child/file.py diff --git a/src/test/multiRootWkspc/workspace1/.vscode/settings.json b/src/testMultiRootWkspc/workspace1/.vscode/settings.json similarity index 100% rename from src/test/multiRootWkspc/workspace1/.vscode/settings.json rename to src/testMultiRootWkspc/workspace1/.vscode/settings.json diff --git a/src/test/multiRootWkspc/workspace1/.vscode/tags b/src/testMultiRootWkspc/workspace1/.vscode/tags similarity index 100% rename from src/test/multiRootWkspc/workspace1/.vscode/tags rename to src/testMultiRootWkspc/workspace1/.vscode/tags diff --git a/src/test/multiRootWkspc/workspace1/file.py b/src/testMultiRootWkspc/workspace1/file.py similarity index 100% rename from src/test/multiRootWkspc/workspace1/file.py rename to src/testMultiRootWkspc/workspace1/file.py diff --git a/src/test/multiRootWkspc/workspace2/.vscode/settings.json b/src/testMultiRootWkspc/workspace2/.vscode/settings.json similarity index 66% rename from src/test/multiRootWkspc/workspace2/.vscode/settings.json rename to src/testMultiRootWkspc/workspace2/.vscode/settings.json index 385728982cfa..750d19764931 100644 --- a/src/test/multiRootWkspc/workspace2/.vscode/settings.json +++ b/src/testMultiRootWkspc/workspace2/.vscode/settings.json @@ -1,4 +1,4 @@ { "python.workspaceSymbols.tagFilePath": "${workspaceRoot}/workspace2.tags.file", - "python.workspaceSymbols.enabled": true + "python.workspaceSymbols.enabled": false } diff --git a/src/test/multiRootWkspc/workspace2/file.py b/src/testMultiRootWkspc/workspace2/file.py similarity index 100% rename from src/test/multiRootWkspc/workspace2/file.py rename to src/testMultiRootWkspc/workspace2/file.py diff --git a/src/testMultiRootWkspc/workspace2/workspace2.tags.file b/src/testMultiRootWkspc/workspace2/workspace2.tags.file new file mode 100644 index 000000000000..375785e2a94e --- /dev/null +++ b/src/testMultiRootWkspc/workspace2/workspace2.tags.file @@ -0,0 +1,24 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +Foo C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^class Foo(object):$/;" kind:class line:5 +Workspace2Class C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\workspace2File.py /^class Workspace2Class(object):$/;" kind:class line:5 +__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\workspace2File.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\workspace2File.py /^__revision__ = None$/;" kind:variable line:3 +file.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py 1;" kind:file line:1 +meth1 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1OfWorkspace2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\workspace2File.py /^ def meth1OfWorkspace2(self, arg):$/;" kind:member line:11 +meth2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\file.py /^ def meth8(self):$/;" kind:member line:80 +workspace2File.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace2\\workspace2File.py 1;" kind:file line:1 diff --git a/src/test/multiRootWkspc/workspace2/workspace2File.py b/src/testMultiRootWkspc/workspace2/workspace2File.py similarity index 100% rename from src/test/multiRootWkspc/workspace2/workspace2File.py rename to src/testMultiRootWkspc/workspace2/workspace2File.py diff --git a/src/test/multiRootWkspc/workspace3/.vscode/settings.json b/src/testMultiRootWkspc/workspace3/.vscode/settings.json similarity index 100% rename from src/test/multiRootWkspc/workspace3/.vscode/settings.json rename to src/testMultiRootWkspc/workspace3/.vscode/settings.json diff --git a/src/test/multiRootWkspc/workspace3/file.py b/src/testMultiRootWkspc/workspace3/file.py similarity index 100% rename from src/test/multiRootWkspc/workspace3/file.py rename to src/testMultiRootWkspc/workspace3/file.py diff --git a/src/test/multiRootWkspc/workspace3/workspace3.tags.file b/src/testMultiRootWkspc/workspace3/workspace3.tags.file similarity index 51% rename from src/test/multiRootWkspc/workspace3/workspace3.tags.file rename to src/testMultiRootWkspc/workspace3/workspace3.tags.file index 9a141392d6ae..3a65841e2aff 100644 --- a/src/test/multiRootWkspc/workspace3/workspace3.tags.file +++ b/src/testMultiRootWkspc/workspace3/workspace3.tags.file @@ -5,15 +5,15 @@ !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ !_TAG_PROGRAM_URL https://ctags.io/ /official site/ !_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ -Foo C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^class Foo(object):$/;" kind:class line:5 -__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def __init__(self):$/;" kind:member line:8 -__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^__revision__ = None$/;" kind:variable line:3 -file.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py 1;" kind:file line:1 -meth1 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 -meth2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 -meth3 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth3(self):$/;" kind:member line:21 -meth4 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth4(self):$/;" kind:member line:28 -meth5 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth5(self):$/;" kind:member line:38 -meth6 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth6(self):$/;" kind:member line:53 -meth7 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth7(self):$/;" kind:member line:68 -meth8 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\test\\multiRootWkspc\\workspace3\\file.py /^ def meth8(self):$/;" kind:member line:80 +Foo C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^class Foo(object):$/;" kind:class line:5 +__init__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def __init__(self):$/;" kind:member line:8 +__revision__ C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^__revision__ = None$/;" kind:variable line:3 +file.py C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py 1;" kind:file line:1 +meth1 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth2 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 C:\\Users\\dojayama\\.vscode\\extensions\\pythonVSCode\\src\\testMultiRootWkspc\\workspace3\\file.py /^ def meth8(self):$/;" kind:member line:80 From a81909628336c56e7bb633664ab616c95fa6004d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 14:31:15 -0700 Subject: [PATCH 32/57] test where failing --- src/test/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index d1d47b46526f..eba20fd38c3d 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -12,7 +12,7 @@ import { initializePython, IS_MULTI_ROOT_TEST } from './initialize'; // a possible error to the callback or null if none. const testRunner = require('vscode/lib/testrunner'); -const invert = IS_MULTI_ROOT_TEST ? undefined : 'invert'; +// const invert = IS_MULTI_ROOT_TEST ? undefined : 'invert'; // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info @@ -20,8 +20,8 @@ testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results timeout: 25000, - grep : 'Multiroot', - invert + grep: 'Multiroot', + invert: 'invert' }); initializePython(); From 672d625cb3e500e39813412c93f9c0f95c436ef3 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 14:41:04 -0700 Subject: [PATCH 33/57] properly determine root workspace --- src/test/common.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/test/common.ts b/src/test/common.ts index 1434e3cdf5ef..5e7fd7f36371 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -2,8 +2,8 @@ import * as path from 'path'; import { ConfigurationTarget, Uri, workspace } from 'vscode'; import { PythonSettings } from '../client/common/configSettings'; -export const fileInNonRootWorkspace = path.join(__dirname, '..', '..', 'src', 'test', 'symbolFiles'); -export const rootWorkspaceUri = workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; +const fileInNonRootWorkspace = path.join(__dirname, '..', '..', 'src', 'test', 'pythonFiles', 'dummy.py'); +export const rootWorkspaceUri = getWorkspaceRoot(); export type PythonSettingKeys = 'workspaceSymbols.enabled' | 'pythonPath' | 'linting.lintOnSave' | 'linting.lintOnTextChange' | @@ -17,4 +17,12 @@ export async function updateSetting(setting: PythonSettingKeys, value: any, reso let settings = workspace.getConfiguration('python', resource); await settings.update(setting, value, configTarget); PythonSettings.dispose(); +} + +function getWorkspaceRoot() { + if (!Array.isArray(workspace.workspaceFolders) || workspace.workspaceFolders.length === 0) { + return Uri.file(path.join(__dirname, '..', '..', 'src', 'test')); + } + + return workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; } \ No newline at end of file From 48ace91ccaccabd0584825ec747ad312714d4a93 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 15:44:13 -0700 Subject: [PATCH 34/57] fix pytest unit test --- src/test/unittests/pytest.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/unittests/pytest.test.ts b/src/test/unittests/pytest.test.ts index 7d5dd23fc6e4..f3ea7e9d6712 100644 --- a/src/test/unittests/pytest.test.ts +++ b/src/test/unittests/pytest.test.ts @@ -162,7 +162,7 @@ suite('Unit Tests (PyTest)', () => { test('Setting cwd should return tests', async () => { - await updateSetting('unitTest.pyTestArgs', ['-s=./tests', '-p=test*.py'], rootWorkspaceUri, configTarget); + await updateSetting('unitTest.pyTestArgs', ['-k=test_'], rootWorkspaceUri, configTarget); createTestManager(unitTestTestFilesCwdPath); const tests = await testManager.discoverTests(true, true); From 61d7b8c0c995d95006d00b20ea094497814fe231 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 15:55:03 -0700 Subject: [PATCH 35/57] delete files --- .../testFiles/standard/.cache/v/cache/lastfailed | 3 --- src/test/unittests/unittest.test.ts | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 src/test/pythonFiles/testFiles/standard/.cache/v/cache/lastfailed diff --git a/src/test/pythonFiles/testFiles/standard/.cache/v/cache/lastfailed b/src/test/pythonFiles/testFiles/standard/.cache/v/cache/lastfailed deleted file mode 100644 index 5ca32881aadf..000000000000 --- a/src/test/pythonFiles/testFiles/standard/.cache/v/cache/lastfailed +++ /dev/null @@ -1,3 +0,0 @@ -{ - "test_root.py::Test_Root_test1::test_Root_A": true -} \ No newline at end of file diff --git a/src/test/unittests/unittest.test.ts b/src/test/unittests/unittest.test.ts index b443d7a260b4..211423086476 100644 --- a/src/test/unittests/unittest.test.ts +++ b/src/test/unittests/unittest.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; import * as path from 'path'; -import * as configSettings from '../../client/common/configSettings'; +import * as fs from 'fs-extra'; import * as unittest from '../../client/unittests/unittest/main'; import { initialize, initializeTest, IS_MULTI_ROOT_TEST } from './../initialize'; import { TestsToRun } from '../../client/unittests/common/contracts'; @@ -28,10 +28,11 @@ suite('Unit Tests (unittest)', () => { await initialize(); await updateSetting('unitTest.unittestArgs', defaultUnitTestArgs, rootWorkspaceUri, configTarget); }); - setup(() => { + setup(async () => { outChannel = new MockOutputChannel('Python Test Log'); testResultDisplay = new TestResultDisplay(outChannel); - return initializeTest(); + await fs.remove(path.join(UNITTEST_TEST_FILES_PATH, '.cache')).catch(() => undefined); + await initializeTest(); }); teardown(() => { outChannel.dispose(); From fc10a55776224fc9706e7d0cca6b25194e3efb6c Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 16:28:23 -0700 Subject: [PATCH 36/57] add awaiter --- src/test/initialize.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 7303b5b9353f..4c591417f4bb 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -4,17 +4,17 @@ // //First thing to be executed -process.env['PYTHON_DONJAYAMANNE_TEST'] = "1"; +process.env['PYTHON_DONJAYAMANNE_TEST'] = '1'; // The module 'assert' provides assertion methods from node -import * as assert from "assert"; +import * as assert from 'assert'; import * as fs from 'fs'; // You can import and use all API from the 'vscode' module // as well as import your extension to test it -import * as vscode from "vscode"; -import * as path from "path"; -let dummyPythonFile = path.join(__dirname, "..", "..", "src", "test", "pythonFiles", "dummy.py"); +import * as vscode from 'vscode'; +import * as path from 'path'; +let dummyPythonFile = path.join(__dirname, '..', '..', 'src', 'test', 'pythonFiles', 'dummy.py'); let configSettings: any = undefined; export async function initialize(): Promise { @@ -98,8 +98,11 @@ export const TEST_TIMEOUT = 25000; export const IS_MULTI_ROOT_TEST = isMultitrootTest(); // Ability to use custom python environments for testing -export function initializePython() { +export async function initializePython() { const pythonConfig = vscode.workspace.getConfiguration('python'); - pythonConfig.update('pythonPath', PYTHON_PATH); + const value = pythonConfig.inspect('pythonPath'); + if (value && value.workspaceValue !== PYTHON_PATH) { + return await pythonConfig.update('pythonPath', PYTHON_PATH, vscode.ConfigurationTarget.Workspace); + } } From 9ab5e146ba0c8135d3610886419c8e89a0caf791 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 16:39:52 -0700 Subject: [PATCH 37/57] use a path that works on multiple os --- src/test/interpreters/display.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/interpreters/display.test.ts b/src/test/interpreters/display.test.ts index 203c1b7f4d2b..8c5b3b517c2b 100644 --- a/src/test/interpreters/display.test.ts +++ b/src/test/interpreters/display.test.ts @@ -56,7 +56,7 @@ suite('Interpreters Display', () => { const displayNameProvider = new MockInterpreterVersionProvider(displayName, true); const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value - const pythonPath = 'c:/some/unknonw/Python Interpreter.exe'; + const pythonPath = 'UnknownInterpreter'; await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); await display.refresh(); @@ -115,7 +115,7 @@ suite('Interpreters Display', () => { const displayNameProvider = new MockInterpreterVersionProvider('', true); const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value - const pythonPath = 'c:/some/unknonw/Python Interpreter.exe'; + const pythonPath = 'UnknownInterpreter'; await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); await display.refresh(); From fc66c9a28781c8434309f6b69574adee2b36ad06 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 19:50:04 -0700 Subject: [PATCH 38/57] fixes --- src/test/initialize.ts | 1 - src/test/interpreters/display.test.ts | 4 +-- .../shebangCodeLenseProvider.test.ts | 34 ++++++++----------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 4c591417f4bb..29730736b07a 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -30,7 +30,6 @@ export async function initialize(): Promise { }); } export async function initializeTest(): Promise { - await initializePython(); await closeActiveWindows(); if (!configSettings) { configSettings = await require('../client/common/configSettings'); diff --git a/src/test/interpreters/display.test.ts b/src/test/interpreters/display.test.ts index 8c5b3b517c2b..f90a45f5ee75 100644 --- a/src/test/interpreters/display.test.ts +++ b/src/test/interpreters/display.test.ts @@ -57,7 +57,7 @@ suite('Interpreters Display', () => { const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value const pythonPath = 'UnknownInterpreter'; - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); + PythonSettings.getInstance().pythonPath = pythonPath; await display.refresh(); const defaultDisplayName = `${path.basename(pythonPath)} [Environment]`; @@ -116,7 +116,7 @@ suite('Interpreters Display', () => { const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value const pythonPath = 'UnknownInterpreter'; - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); + PythonSettings.getInstance().pythonPath = pythonPath; await display.refresh(); assert.equal(statusBar.text, '$(alert) Select Python Environment', 'Incorrect display name'); diff --git a/src/test/providers/shebangCodeLenseProvider.test.ts b/src/test/providers/shebangCodeLenseProvider.test.ts index 59c7ecf2d5d6..a9b4a306e90e 100644 --- a/src/test/providers/shebangCodeLenseProvider.test.ts +++ b/src/test/providers/shebangCodeLenseProvider.test.ts @@ -2,7 +2,7 @@ import * as assert from 'assert'; import * as path from 'path'; import * as vscode from 'vscode'; import * as child_process from 'child_process'; -import { IS_WINDOWS } from '../../client/common/configSettings'; +import { IS_WINDOWS, PythonSettings } from '../../client/common/configSettings'; import { rootWorkspaceUri, updateSetting } from '../common'; import { ShebangCodeLensProvider } from '../../client/providers/shebangCodeLensProvider'; import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; @@ -22,9 +22,8 @@ suite('Shebang detection', () => { }); setup(() => initializeTest()); - test('Shebang available, CodeLens showing', async () => { - const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; - await updateSetting('pythonPath', 'someUnknownInterpreter', rootWorkspaceUri, configTarget); + test('A code lens will appear when sheban python and python in settings are different', async () => { + PythonSettings.getInstance().pythonPath = 'someUnknownInterpreter'; const editor = await openFile(fileShebang); const codeLenses = await setupCodeLens(editor); @@ -34,29 +33,25 @@ suite('Shebang detection', () => { assert.equal(codeLens.command.command, 'python.setShebangInterpreter'); }); - test('Shebang available, CodeLens hiding', async () => { - const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, configTarget); + test('Code lens will not appear when sheban python and python in settings are the same', async () => { + PythonSettings.dispose(); + const pythonPath = await getFullyQualifiedPathToInterpreter(PythonSettings.getInstance().pythonPath); + PythonSettings.getInstance().pythonPath = pythonPath; const editor = await openFile(fileShebang); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); }); - test('Shebang not available (invalid shebang)', async () => { - const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, configTarget); + test('Code lens will not appear when sheban python is invalid', async () => { const editor = await openFile(fileShebangInvalid); const codeLenses = await setupCodeLens(editor); - assert.equal(codeLenses.length, 0, 'CodeLens available although shebang is invalid'); + assert.equal(codeLenses.length, 0, 'CodeLens available even when shebang is invalid'); }); if (!IS_WINDOWS) { - test('Shebang available, CodeLens showing with env', async () => { - const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; - await updateSetting('pythonPath', 'p1', rootWorkspaceUri, configTarget); + test('A code lens will appear when shebang python uses env and python settings are different', async () => { + PythonSettings.getInstance().pythonPath = 'p1'; const editor = await openFile(fileShebangEnv); const codeLenses = await setupCodeLens(editor); @@ -67,17 +62,16 @@ suite('Shebang detection', () => { }); - test('Shebang available, CodeLens hiding with env', async () => { + test('Code lens will not appear even when shebang python uses env and python settings are the same', async () => { const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - const configTarget = IS_MULTI_ROOT_TEST ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, configTarget); + PythonSettings.getInstance().pythonPath = pythonPath; const editor = await openFile(fileShebangEnv); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); }); } - test('Shebang missing, CodeLens hiding', async () => { + test('Code lens will not appear as there is no shebang', async () => { const editor = await openFile(filePlain); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although no shebang'); From 121ff0fab32017daae5a49a4144e6fd7a89d6654 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 19:50:49 -0700 Subject: [PATCH 39/57] uncomment --- src/test/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index eba20fd38c3d..076b4cf9aa84 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -12,7 +12,7 @@ import { initializePython, IS_MULTI_ROOT_TEST } from './initialize'; // a possible error to the callback or null if none. const testRunner = require('vscode/lib/testrunner'); -// const invert = IS_MULTI_ROOT_TEST ? undefined : 'invert'; +const invert = IS_MULTI_ROOT_TEST ? undefined : 'invert'; // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info @@ -21,7 +21,7 @@ testRunner.configure({ useColors: true, // colored output from test results timeout: 25000, grep: 'Multiroot', - invert: 'invert' + invert }); initializePython(); From 6c01e78585549d4f5953721395a3d51cc43fc9be Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 19:57:41 -0700 Subject: [PATCH 40/57] invert --- src/test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/index.ts b/src/test/index.ts index 076b4cf9aa84..878b36183ba2 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -21,7 +21,7 @@ testRunner.configure({ useColors: true, // colored output from test results timeout: 25000, grep: 'Multiroot', - invert + invert: 'invert' }); initializePython(); From b1b26778c80bd8ad6bec5c4a1cfdbfa88ac22d74 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 20:08:01 -0700 Subject: [PATCH 41/57] debug statements --- src/test/common.ts | 13 ++++++++++--- src/test/index.ts | 5 +++-- src/test/initialize.ts | 5 ++++- src/test/multiRootTest.ts | 1 + 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/test/common.ts b/src/test/common.ts index 5e7fd7f36371..4116cc294c00 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -20,9 +20,16 @@ export async function updateSetting(setting: PythonSettingKeys, value: any, reso } function getWorkspaceRoot() { + //DEBUGGER + console.log('start'); if (!Array.isArray(workspace.workspaceFolders) || workspace.workspaceFolders.length === 0) { return Uri.file(path.join(__dirname, '..', '..', 'src', 'test')); } - - return workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; -} \ No newline at end of file + console.log('other'); + try { + return workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; + } + catch (ex) { + console.error('kaboom'); + } +} diff --git a/src/test/index.ts b/src/test/index.ts index 878b36183ba2..24523b20d40e 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -11,6 +11,9 @@ import { initializePython, IS_MULTI_ROOT_TEST } from './initialize'; // to report the results back to the caller. When the tests are finished, return // a possible error to the callback or null if none. +//DEBUGGER +console.log('index.ts'); + const testRunner = require('vscode/lib/testrunner'); const invert = IS_MULTI_ROOT_TEST ? undefined : 'invert'; @@ -24,6 +27,4 @@ testRunner.configure({ invert: 'invert' }); -initializePython(); - module.exports = testRunner; diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 29730736b07a..a99c1866b406 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -5,7 +5,8 @@ //First thing to be executed process.env['PYTHON_DONJAYAMANNE_TEST'] = '1'; - +//DEBUGGER +console.log('start initialize'); // The module 'assert' provides assertion methods from node import * as assert from 'assert'; import * as fs from 'fs'; @@ -98,6 +99,8 @@ export const IS_MULTI_ROOT_TEST = isMultitrootTest(); // Ability to use custom python environments for testing export async function initializePython() { + //DEBUGGER + console.log('initializePythonPath'); const pythonConfig = vscode.workspace.getConfiguration('python'); const value = pythonConfig.inspect('pythonPath'); if (value && value.workspaceValue !== PYTHON_PATH) { diff --git a/src/test/multiRootTest.ts b/src/test/multiRootTest.ts index 4bbd5d7bb9b2..57bf5368ccb7 100644 --- a/src/test/multiRootTest.ts +++ b/src/test/multiRootTest.ts @@ -3,6 +3,7 @@ import * as path from 'path'; process.env.CODE_TESTS_WORKSPACE = path.join(__dirname, '..', '..', 'src', 'testMultiRootWkspc', 'multi.code-workspace'); function start() { + console.log('start Multiroot tests'); require('../../node_modules/vscode/bin/test'); } start(); From 76e413910de2f220e5695edd13ec97d6b92bea02 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 20:15:26 -0700 Subject: [PATCH 42/57] use default workspace --- src/test/common.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/common.ts b/src/test/common.ts index 4116cc294c00..c30e340e3485 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -25,6 +25,9 @@ function getWorkspaceRoot() { if (!Array.isArray(workspace.workspaceFolders) || workspace.workspaceFolders.length === 0) { return Uri.file(path.join(__dirname, '..', '..', 'src', 'test')); } + if (workspace.workspaceFolders.length === 1) { + return workspace.workspaceFolders[0].uri; + } console.log('other'); try { return workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; From 35fba77c4d1c7167695e0b8a6fd355bca945c32e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 20:28:21 -0700 Subject: [PATCH 43/57] reverted unwanted changes --- src/test/common.ts | 10 +--------- src/test/index.ts | 5 +---- src/test/initialize.ts | 13 +++---------- src/test/interpreters/display.test.ts | 4 ++-- src/test/providers/shebangCodeLenseProvider.test.ts | 10 ++++++---- 5 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/test/common.ts b/src/test/common.ts index c30e340e3485..93be892924ce 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -20,19 +20,11 @@ export async function updateSetting(setting: PythonSettingKeys, value: any, reso } function getWorkspaceRoot() { - //DEBUGGER - console.log('start'); if (!Array.isArray(workspace.workspaceFolders) || workspace.workspaceFolders.length === 0) { return Uri.file(path.join(__dirname, '..', '..', 'src', 'test')); } if (workspace.workspaceFolders.length === 1) { return workspace.workspaceFolders[0].uri; } - console.log('other'); - try { - return workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; - } - catch (ex) { - console.error('kaboom'); - } + return workspace.getWorkspaceFolder(Uri.file(fileInNonRootWorkspace)).uri; } diff --git a/src/test/index.ts b/src/test/index.ts index 24523b20d40e..defee3a510e4 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -11,9 +11,6 @@ import { initializePython, IS_MULTI_ROOT_TEST } from './initialize'; // to report the results back to the caller. When the tests are finished, return // a possible error to the callback or null if none. -//DEBUGGER -console.log('index.ts'); - const testRunner = require('vscode/lib/testrunner'); const invert = IS_MULTI_ROOT_TEST ? undefined : 'invert'; @@ -24,7 +21,7 @@ testRunner.configure({ useColors: true, // colored output from test results timeout: 25000, grep: 'Multiroot', - invert: 'invert' + invert }); module.exports = testRunner; diff --git a/src/test/initialize.ts b/src/test/initialize.ts index a99c1866b406..e0cdddb3dc0c 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -3,16 +3,10 @@ // Please refer to their documentation on https://mochajs.org/ for help. // -//First thing to be executed -process.env['PYTHON_DONJAYAMANNE_TEST'] = '1'; -//DEBUGGER -console.log('start initialize'); -// The module 'assert' provides assertion methods from node -import * as assert from 'assert'; -import * as fs from 'fs'; - // You can import and use all API from the 'vscode' module // as well as import your extension to test it +import * as assert from 'assert'; +import * as fs from 'fs'; import * as vscode from 'vscode'; import * as path from 'path'; let dummyPythonFile = path.join(__dirname, '..', '..', 'src', 'test', 'pythonFiles', 'dummy.py'); @@ -31,6 +25,7 @@ export async function initialize(): Promise { }); } export async function initializeTest(): Promise { + await initializePython(); await closeActiveWindows(); if (!configSettings) { configSettings = await require('../client/common/configSettings'); @@ -99,8 +94,6 @@ export const IS_MULTI_ROOT_TEST = isMultitrootTest(); // Ability to use custom python environments for testing export async function initializePython() { - //DEBUGGER - console.log('initializePythonPath'); const pythonConfig = vscode.workspace.getConfiguration('python'); const value = pythonConfig.inspect('pythonPath'); if (value && value.workspaceValue !== PYTHON_PATH) { diff --git a/src/test/interpreters/display.test.ts b/src/test/interpreters/display.test.ts index f90a45f5ee75..8c5b3b517c2b 100644 --- a/src/test/interpreters/display.test.ts +++ b/src/test/interpreters/display.test.ts @@ -57,7 +57,7 @@ suite('Interpreters Display', () => { const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value const pythonPath = 'UnknownInterpreter'; - PythonSettings.getInstance().pythonPath = pythonPath; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); await display.refresh(); const defaultDisplayName = `${path.basename(pythonPath)} [Environment]`; @@ -116,7 +116,7 @@ suite('Interpreters Display', () => { const display = new InterpreterDisplay(statusBar, provider, new VirtualEnvironmentManager([]), displayNameProvider); // Change interpreter to an invalid value const pythonPath = 'UnknownInterpreter'; - PythonSettings.getInstance().pythonPath = pythonPath; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); await display.refresh(); assert.equal(statusBar.text, '$(alert) Select Python Environment', 'Incorrect display name'); diff --git a/src/test/providers/shebangCodeLenseProvider.test.ts b/src/test/providers/shebangCodeLenseProvider.test.ts index a9b4a306e90e..13b47123956c 100644 --- a/src/test/providers/shebangCodeLenseProvider.test.ts +++ b/src/test/providers/shebangCodeLenseProvider.test.ts @@ -7,6 +7,7 @@ import { rootWorkspaceUri, updateSetting } from '../common'; import { ShebangCodeLensProvider } from '../../client/providers/shebangCodeLensProvider'; import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; import { getFirstNonEmptyLineFromMultilineString } from '../../client/interpreter/helpers'; +import { ConfigurationTarget } from 'vscode'; const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'shebang'); const fileShebang = path.join(autoCompPath, 'shebang.py'); @@ -23,7 +24,8 @@ suite('Shebang detection', () => { setup(() => initializeTest()); test('A code lens will appear when sheban python and python in settings are different', async () => { - PythonSettings.getInstance().pythonPath = 'someUnknownInterpreter'; + const pythonPath = 'someUnknownInterpreter'; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebang); const codeLenses = await setupCodeLens(editor); @@ -36,7 +38,7 @@ suite('Shebang detection', () => { test('Code lens will not appear when sheban python and python in settings are the same', async () => { PythonSettings.dispose(); const pythonPath = await getFullyQualifiedPathToInterpreter(PythonSettings.getInstance().pythonPath); - PythonSettings.getInstance().pythonPath = pythonPath; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebang); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); @@ -51,7 +53,7 @@ suite('Shebang detection', () => { if (!IS_WINDOWS) { test('A code lens will appear when shebang python uses env and python settings are different', async () => { - PythonSettings.getInstance().pythonPath = 'p1'; + await updateSetting('pythonPath', 'p1', rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebangEnv); const codeLenses = await setupCodeLens(editor); @@ -64,7 +66,7 @@ suite('Shebang detection', () => { test('Code lens will not appear even when shebang python uses env and python settings are the same', async () => { const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - PythonSettings.getInstance().pythonPath = pythonPath; + await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebangEnv); const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); From 22f0301925b94635a66e9fe3be262faa927acbd9 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 20:33:14 -0700 Subject: [PATCH 44/57] oops --- src/test/initialize.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/initialize.ts b/src/test/initialize.ts index e0cdddb3dc0c..91721a2b44f5 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -11,6 +11,9 @@ import * as vscode from 'vscode'; import * as path from 'path'; let dummyPythonFile = path.join(__dirname, '..', '..', 'src', 'test', 'pythonFiles', 'dummy.py'); +//First thing to be executed +process.env['PYTHON_DONJAYAMANNE_TEST'] = '1'; + let configSettings: any = undefined; export async function initialize(): Promise { await initializePython(); From b9eaeafa5bfe22720a4e3a768692d75570f999ab Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 20:50:57 -0700 Subject: [PATCH 45/57] test unittests only --- .vscode/launch.json | 4 ++-- src/test/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e33a7ad07f0a..8c80fc1679cd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -33,7 +33,7 @@ "outFiles": [ "${workspaceRoot}/out/**/*.js" ], - "preLaunchTask": "npm" + "preLaunchTask": "compile" }, { "name": "Launch Extension as debugServer", // https://code.visualstudio.com/docs/extensions/example-debuggers @@ -65,7 +65,7 @@ "outFiles": [ "${workspaceRoot}/out/**/*.js" ], - "preLaunchTask": "npm" + "preLaunchTask": "compile" }, { "name": "Launch Multiroot Tests", diff --git a/src/test/index.ts b/src/test/index.ts index defee3a510e4..6f437a75c7db 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -20,8 +20,8 @@ testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results timeout: 25000, - grep: 'Multiroot', - invert + grep: 'Unit Tests', + // invert }); module.exports = testRunner; From 1dd4cb44d8a85c615bf81c410dfc125623408497 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 20:53:42 -0700 Subject: [PATCH 46/57] more logging --- src/test/initialize.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 91721a2b44f5..c5194221a7ea 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -28,13 +28,18 @@ export async function initialize(): Promise { }); } export async function initializeTest(): Promise { + console.log('1'); await initializePython(); + console.log('2'); await closeActiveWindows(); + console.log('3'); if (!configSettings) { configSettings = await require('../client/common/configSettings'); } + console.log('4'); // Dispose any cached python settings (used only in test env) configSettings.PythonSettings.dispose(); + console.log('5'); } export async function wait(timeoutMilliseconds: number) { From 26a8fdcdade52e779b5898c3bdb7e62123061b11 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 21:13:56 -0700 Subject: [PATCH 47/57] partial fixes to unit tests --- src/client/unittests/common/baseTestManager.ts | 5 ++++- src/client/unittests/configuration.ts | 10 ++++++---- src/client/unittests/main.ts | 15 +++++++++++---- src/client/unittests/nosetest/collector.ts | 5 ++--- src/client/unittests/nosetest/main.ts | 6 ++---- src/client/unittests/nosetest/runner.ts | 4 ++-- src/client/unittests/pytest/collector.ts | 4 +--- src/client/unittests/pytest/main.ts | 6 ++---- src/client/unittests/pytest/runner.ts | 5 ++--- src/client/unittests/unittest/collector.ts | 6 ++---- src/client/unittests/unittest/main.ts | 6 ++---- src/client/unittests/unittest/runner.ts | 6 ++---- 12 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/client/unittests/common/baseTestManager.ts b/src/client/unittests/common/baseTestManager.ts index 1dc38f7259b2..e1f3dde1e5ab 100644 --- a/src/client/unittests/common/baseTestManager.ts +++ b/src/client/unittests/common/baseTestManager.ts @@ -4,6 +4,7 @@ import * as vscode from 'vscode'; import { resetTestResults, displayTestErrorMessage, storeDiscoveredTests } from './testUtils'; import { Installer, Product } from '../../common/installer'; import { isNotInstalledError } from '../../common/helpers'; +import { IPythonSettings, PythonSettings } from '../../common/configSettings'; export abstract class BaseTestManager { private tests: Tests; @@ -25,9 +26,11 @@ export abstract class BaseTestManager { this.cancellationTokenSource.cancel(); } } + protected readonly settings: IPythonSettings; constructor(private testProvider: string, private product: Product, protected rootDirectory: string, protected outputChannel: vscode.OutputChannel) { this._status = TestStatus.Unknown; this.installer = new Installer(); + this.settings = PythonSettings.getInstance(vscode.Uri.file(this.rootDirectory)); } public reset() { this._status = TestStatus.Unknown; @@ -179,4 +182,4 @@ export abstract class BaseTestManager { }); } abstract runTestImpl(tests: Tests, testsToRun?: TestsToRun, runFailedTests?: boolean, debug?: boolean): Promise; -} \ No newline at end of file +} diff --git a/src/client/unittests/configuration.ts b/src/client/unittests/configuration.ts index 0c1f47f7bf45..cd2b7614179e 100644 --- a/src/client/unittests/configuration.ts +++ b/src/client/unittests/configuration.ts @@ -9,8 +9,6 @@ import * as unittest from './unittest/testConfigurationManager'; import { getSubDirectories } from '../common/utils'; import * as path from 'path'; -const settings = PythonSettings.getInstance(); - function promptToEnableAndConfigureTestFramework(outputChannel: vscode.OutputChannel, messageToDisplay: string = 'Select a test framework/tool to enable', enableOnly: boolean = false): Thenable { const items = [{ label: 'unittest', @@ -76,8 +74,9 @@ function promptToEnableAndConfigureTestFramework(outputChannel: vscode.OutputCha // Cuz we don't want the test engine (in main.ts file - tests get discovered when config changes are detected) // to start discovering tests when tests haven't been configured properly function enableTest(): Thenable { + // TODO: Enable multi workspace root support const pythonConfig = vscode.workspace.getConfiguration('python'); - if (settings.unitTest.promptToConfigure) { + if (pythonConfig.get('unitTest.promptToConfigure')) { return configMgr.enable(); } return pythonConfig.update('unitTest.promptToConfigure', undefined).then(() => { @@ -94,6 +93,8 @@ function promptToEnableAndConfigureTestFramework(outputChannel: vscode.OutputCha }); } export function displayTestFrameworkError(outputChannel: vscode.OutputChannel): Thenable { + // TODO: Enable multi workspace root support + const settings = PythonSettings.getInstance(); let enabledCount = settings.unitTest.pyTestEnabled ? 1 : 0; enabledCount += settings.unitTest.nosetestsEnabled ? 1 : 0; enabledCount += settings.unitTest.unittestEnabled ? 1 : 0; @@ -111,6 +112,7 @@ export function displayTestFrameworkError(outputChannel: vscode.OutputChannel): } } export function displayPromptToEnableTests(rootDir: string, outputChannel: vscode.OutputChannel): Thenable { + const settings = PythonSettings.getInstance(vscode.Uri.file(rootDir)); if (settings.unitTest.pyTestEnabled || settings.unitTest.nosetestsEnabled || settings.unitTest.unittestEnabled) { @@ -147,4 +149,4 @@ function checkIfHasTestDirs(rootDir: string): Promise { return getSubDirectories(rootDir).then(subDirs => { return subDirs.map(dir => path.relative(rootDir, dir)).filter(dir => dir.match(/test/i)).length > 0; }); -} \ No newline at end of file +} diff --git a/src/client/unittests/main.ts b/src/client/unittests/main.ts index 6ab1f22b2e71..2fab50340bb5 100644 --- a/src/client/unittests/main.ts +++ b/src/client/unittests/main.ts @@ -14,14 +14,13 @@ import { } from './common/contracts'; import { resolveValueAsTestToRun, getDiscoveredTests } from './common/testUtils'; import { BaseTestManager } from './common/baseTestManager'; -import { PythonSettings } from '../common/configSettings'; +import { PythonSettings, IUnitTestSettings } from '../common/configSettings'; import { TestResultDisplay } from './display/main'; import { TestDisplay } from './display/picker'; import { activateCodeLenses } from './codeLenses/main'; import { displayTestFrameworkError } from './configuration'; import { PythonSymbolProvider } from '../providers/symbolProvider'; -const settings = PythonSettings.getInstance(); let testManager: BaseTestManager | undefined | null; let pyTestManager: pytest.TestManager | undefined | null; let unittestManager: unittest.TestManager | undefined | null; @@ -32,6 +31,9 @@ let outChannel: vscode.OutputChannel; let onDidChange: vscode.EventEmitter = new vscode.EventEmitter(); export function activate(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel, symboldProvider: PythonSymbolProvider) { + // TODO: Add multi workspace support + const settings = PythonSettings.getInstance(); + uniTestSettingsString = JSON.stringify(settings.unitTest); context.subscriptions.push({ dispose: dispose }); outChannel = outputChannel; let disposables = registerCommands(); @@ -51,6 +53,8 @@ export function activate(context: vscode.ExtensionContext, outputChannel: vscode } function getTestWorkingDirectory() { + // TODO: Add multi workspace support + const settings = PythonSettings.getInstance(); return settings.unitTest.cwd && settings.unitTest.cwd.length > 0 ? settings.unitTest.cwd : vscode.workspace.rootPath!; } @@ -184,9 +188,11 @@ function displayStopUI(message: string) { testDisplay = testDisplay ? testDisplay : new TestDisplay(); testDisplay.displayStopTestUI(message); } -let uniTestSettingsString = JSON.stringify(settings.unitTest); +let uniTestSettingsString: string; function onConfigChanged() { + // TODO: Add multi workspace support + const settings = PythonSettings.getInstance(); // Possible that a test framework has been enabled or some settings have changed // Meaning we need to re-load the discovered tests (as something could have changed) const newSettings = JSON.stringify(settings.unitTest); @@ -230,6 +236,7 @@ function onConfigChanged() { } function getTestRunner() { const rootDirectory = getTestWorkingDirectory(); + const settings = PythonSettings.getInstance(vscode.Uri.file(rootDirectory)); if (settings.unitTest.nosetestsEnabled) { return nosetestManager = nosetestManager ? nosetestManager : new nosetests.TestManager(rootDirectory, outChannel); } @@ -317,4 +324,4 @@ function runTestsImpl(arg?: vscode.Uri | TestsToRun | boolean | FlattenedTestFun }); testResultDisplay.DisplayProgressStatus(runPromise, debug); -} \ No newline at end of file +} diff --git a/src/client/unittests/nosetest/collector.ts b/src/client/unittests/nosetest/collector.ts index 7c8888d19459..1926aa694237 100644 --- a/src/client/unittests/nosetest/collector.ts +++ b/src/client/unittests/nosetest/collector.ts @@ -6,9 +6,8 @@ import * as os from 'os'; import { extractBetweenDelimiters, convertFileToPackage, flattenTestFiles } from '../common/testUtils'; import { CancellationToken } from 'vscode'; import { PythonSettings } from '../../common/configSettings'; -import { OutputChannel } from 'vscode'; +import { OutputChannel, Uri } from 'vscode'; -const pythonSettings = PythonSettings.getInstance(); const NOSE_WANT_FILE_PREFIX = 'nose.selector: DEBUG: wantFile '; const NOSE_WANT_FILE_SUFFIX = '.py? True'; const NOSE_WANT_FILE_SUFFIX_WITHOUT_EXT = '? True'; @@ -78,7 +77,7 @@ export function discoverTests(rootDirectory: string, args: string[], token: Canc }); } - return execPythonFile(pythonSettings.unitTest.nosetestPath, args.concat(['--collect-only', '-vvv']), rootDirectory, true) + return execPythonFile(PythonSettings.getInstance(Uri.file(rootDirectory)).unitTest.nosetestPath, args.concat(['--collect-only', '-vvv']), rootDirectory, true) .then(data => { outChannel.appendLine(data); processOutput(data); diff --git a/src/client/unittests/nosetest/main.ts b/src/client/unittests/nosetest/main.ts index 1f3def92cfb8..12ced38c9a7d 100644 --- a/src/client/unittests/nosetest/main.ts +++ b/src/client/unittests/nosetest/main.ts @@ -8,18 +8,16 @@ import { BaseTestManager } from '../common/baseTestManager'; import { runTest } from './runner'; import { Product } from '../../common/installer'; -const settings = PythonSettings.getInstance(); - export class TestManager extends BaseTestManager { constructor(rootDirectory: string, outputChannel: vscode.OutputChannel) { super('nosetest', Product.nosetest, rootDirectory, outputChannel); } discoverTestsImpl(ignoreCache: boolean): Promise { - let args = settings.unitTest.nosetestArgs.slice(0); + let args = this.settings.unitTest.nosetestArgs.slice(0); return discoverTests(this.rootDirectory, args, this.cancellationToken, ignoreCache, this.outputChannel); } runTestImpl(tests: Tests, testsToRun?: TestsToRun, runFailedTests?: boolean, debug?: boolean): Promise { - let args = settings.unitTest.nosetestArgs.slice(0); + let args = this.settings.unitTest.nosetestArgs.slice(0); if (runFailedTests === true && args.indexOf('--failed') === -1) { args.push('--failed'); } diff --git a/src/client/unittests/nosetest/runner.ts b/src/client/unittests/nosetest/runner.ts index fdaba3ff8254..3f6f73bb2532 100644 --- a/src/client/unittests/nosetest/runner.ts +++ b/src/client/unittests/nosetest/runner.ts @@ -1,6 +1,6 @@ 'use strict'; import { createTemporaryFile } from '../../common/helpers'; -import { OutputChannel, CancellationToken } from 'vscode'; +import { OutputChannel, CancellationToken, Uri } from 'vscode'; import { TestsToRun, Tests } from '../common/contracts'; import { updateResults } from '../common/testUtils'; import { updateResultsFromXmlLogFile, PassCalculationFormulae } from '../common/xUnitParser'; @@ -9,7 +9,6 @@ import { PythonSettings } from '../../common/configSettings'; import * as path from 'path'; import { launchDebugger } from '../common/debugLauncher'; -const pythonSettings = PythonSettings.getInstance(); const WITH_XUNIT = '--with-xunit'; const XUNIT_FILE = '--xunit-file'; @@ -61,6 +60,7 @@ export function runTest(rootDirectory: string, tests: Tests, args: string[], tes } return promiseToGetXmlLogFile.then(() => { + const pythonSettings = PythonSettings.getInstance(Uri.file(rootDirectory)); if (debug === true) { const testLauncherFile = path.join(__dirname, '..', '..', '..', '..', 'pythonFiles', 'PythonTools', 'testlauncher.py'); const nosetestlauncherargs = [rootDirectory, 'my_secret', pythonSettings.unitTest.debugPort.toString(), 'nose']; diff --git a/src/client/unittests/pytest/collector.ts b/src/client/unittests/pytest/collector.ts index af36b2b9fb97..eecc8055fa11 100644 --- a/src/client/unittests/pytest/collector.ts +++ b/src/client/unittests/pytest/collector.ts @@ -8,8 +8,6 @@ import * as path from 'path'; import { PythonSettings } from '../../common/configSettings'; import { OutputChannel } from 'vscode'; -const pythonSettings = PythonSettings.getInstance(); - const argsToExcludeForDiscovery = ['-x', '--exitfirst', '--fixtures-per-test', '--pdb', '--runxfail', '--lf', '--last-failed', '--ff', '--failed-first', @@ -85,7 +83,7 @@ export function discoverTests(rootDirectory: string, args: string[], token: vsco }); } - return execPythonFile(pythonSettings.unitTest.pyTestPath, args.concat(['--collect-only']), rootDirectory, false, null, token) + return execPythonFile(PythonSettings.getInstance(vscode.Uri.file(rootDirectory)).unitTest.pyTestPath, args.concat(['--collect-only']), rootDirectory, false, null, token) .then(data => { outChannel.appendLine(data); processOutput(data); diff --git a/src/client/unittests/pytest/main.ts b/src/client/unittests/pytest/main.ts index 8be3477c1b81..06e86e75fd34 100644 --- a/src/client/unittests/pytest/main.ts +++ b/src/client/unittests/pytest/main.ts @@ -1,5 +1,4 @@ 'use strict'; -import { PythonSettings } from '../../common/configSettings'; import { TestsToRun, Tests } from '../common/contracts'; import { runTest } from './runner'; import * as vscode from 'vscode'; @@ -7,17 +6,16 @@ import { discoverTests } from './collector'; import { BaseTestManager } from '../common/baseTestManager'; import { Product } from '../../common/installer'; -const settings = PythonSettings.getInstance(); export class TestManager extends BaseTestManager { constructor(rootDirectory: string, outputChannel: vscode.OutputChannel) { super('pytest', Product.pytest, rootDirectory, outputChannel); } discoverTestsImpl(ignoreCache: boolean): Promise { - let args = settings.unitTest.pyTestArgs.slice(0); + let args = this.settings.unitTest.pyTestArgs.slice(0); return discoverTests(this.rootDirectory, args, this.cancellationToken, ignoreCache, this.outputChannel); } runTestImpl(tests: Tests, testsToRun?: TestsToRun, runFailedTests?: boolean, debug?: boolean): Promise { - let args = settings.unitTest.pyTestArgs.slice(0); + let args = this.settings.unitTest.pyTestArgs.slice(0); if (runFailedTests === true && args.indexOf('--lf') === -1 && args.indexOf('--last-failed') === -1) { args.push('--last-failed'); } diff --git a/src/client/unittests/pytest/runner.ts b/src/client/unittests/pytest/runner.ts index eb564b57c015..963ad150b5c9 100644 --- a/src/client/unittests/pytest/runner.ts +++ b/src/client/unittests/pytest/runner.ts @@ -4,15 +4,13 @@ import { createTemporaryFile } from '../../common/helpers'; import { TestsToRun, Tests } from '../common/contracts'; import { updateResults } from '../common/testUtils'; -import { CancellationToken, OutputChannel } from 'vscode'; +import { CancellationToken, OutputChannel, Uri } from 'vscode'; import { updateResultsFromXmlLogFile, PassCalculationFormulae } from '../common/xUnitParser'; import { run } from '../common/runner'; import { PythonSettings } from '../../common/configSettings'; import * as path from 'path'; import { launchDebugger } from '../common/debugLauncher'; -const pythonSettings = PythonSettings.getInstance(); - export function runTest(rootDirectory: string, tests: Tests, args: string[], testsToRun?: TestsToRun, token?: CancellationToken, outChannel?: OutputChannel, debug?: boolean): Promise { let testPaths = []; if (testsToRun && testsToRun.testFolder) { @@ -39,6 +37,7 @@ export function runTest(rootDirectory: string, tests: Tests, args: string[], tes args = args.filter(arg => arg.trim().startsWith('-')); } const testArgs = testPaths.concat(args, [`--junitxml=${xmlLogFile}`]); + const pythonSettings = PythonSettings.getInstance(Uri.file(rootDirectory)); if (debug) { const testLauncherFile = path.join(__dirname, '..', '..', '..', '..', 'pythonFiles', 'PythonTools', 'testlauncher.py'); const pytestlauncherargs = [rootDirectory, 'my_secret', pythonSettings.unitTest.debugPort.toString(), 'pytest']; diff --git a/src/client/unittests/unittest/collector.ts b/src/client/unittests/unittest/collector.ts index 91affb058814..49e34b4232f0 100644 --- a/src/client/unittests/unittest/collector.ts +++ b/src/client/unittests/unittest/collector.ts @@ -7,8 +7,6 @@ import * as path from 'path'; import { PythonSettings } from '../../common/configSettings'; import { OutputChannel } from 'vscode'; -const pythonSettings = PythonSettings.getInstance(); - export function discoverTests(rootDirectory: string, args: string[], token: vscode.CancellationToken, ignoreCache: boolean, outChannel: OutputChannel): Promise { let startDirectory = '.'; let pattern = 'test*.py'; @@ -73,7 +71,7 @@ for suite in suites._tests: }); } args = []; - return execPythonFile(pythonSettings.pythonPath, args.concat(['-c', pythonScript]), rootDirectory, true, null, token) + return execPythonFile(PythonSettings.getInstance(vscode.Uri.file(rootDirectory)).pythonPath, args.concat(['-c', pythonScript]), rootDirectory, true, null, token) .then(data => { outChannel.appendLine(data); processOutput(data); @@ -152,4 +150,4 @@ function addTestId(rootDirectory: string, testId: string, testFiles: TestFile[]) }; testSuite.functions.push(testFunction); -} \ No newline at end of file +} diff --git a/src/client/unittests/unittest/main.ts b/src/client/unittests/unittest/main.ts index 51cee1b32ac1..665406886146 100644 --- a/src/client/unittests/unittest/main.ts +++ b/src/client/unittests/unittest/main.ts @@ -6,8 +6,6 @@ import * as vscode from 'vscode'; import { discoverTests } from './collector'; import { BaseTestManager } from '../common/baseTestManager'; import { Product } from '../../common/installer'; - -const settings = PythonSettings.getInstance(); export class TestManager extends BaseTestManager { constructor(rootDirectory: string, outputChannel: vscode.OutputChannel) { super('unitest', Product.unittest, rootDirectory, outputChannel); @@ -15,11 +13,11 @@ export class TestManager extends BaseTestManager { configure() { } discoverTestsImpl(ignoreCache: boolean): Promise { - let args = settings.unitTest.unittestArgs.slice(0); + let args = this.settings.unitTest.unittestArgs.slice(0); return discoverTests(this.rootDirectory, args, this.cancellationToken, ignoreCache, this.outputChannel); } runTestImpl(tests: Tests, testsToRun?: TestsToRun, runFailedTests?: boolean, debug?: boolean): Promise { - let args = settings.unitTest.unittestArgs.slice(0); + let args = this.settings.unitTest.unittestArgs.slice(0); if (runFailedTests === true) { testsToRun = { testFile: [], testFolder: [], testSuite: [], testFunction: [] }; testsToRun.testFunction = tests.testFunctions.filter(fn => { diff --git a/src/client/unittests/unittest/runner.ts b/src/client/unittests/unittest/runner.ts index 1435dc8b3062..d6f98e8365aa 100644 --- a/src/client/unittests/unittest/runner.ts +++ b/src/client/unittests/unittest/runner.ts @@ -5,13 +5,11 @@ import * as path from 'path'; import { TestsToRun, Tests, TestStatus } from '../common/contracts'; import { updateResults } from '../common/testUtils'; import { BaseTestManager } from '../common/baseTestManager'; -import { CancellationToken, OutputChannel } from 'vscode'; +import { CancellationToken, OutputChannel, Uri } from 'vscode'; import { run } from '../common/runner'; import { Server } from './socketServer'; import { PythonSettings } from '../../common/configSettings'; import { launchDebugger } from '../common/debugLauncher'; - -const settings = PythonSettings.getInstance(); interface TestStatusMap { status: TestStatus; summaryProperty: string; @@ -96,7 +94,7 @@ export function runTest(testManager: BaseTestManager, rootDirectory: string, tes return launchDebugger(rootDirectory, [testLauncherFile].concat(testArgs), token, outChannel); } else { - return run(settings.pythonPath, [testLauncherFile].concat(testArgs), rootDirectory, token, outChannel); + return run(PythonSettings.getInstance(Uri.file(rootDirectory)).pythonPath, [testLauncherFile].concat(testArgs), rootDirectory, token, outChannel); } } From 75a9e7b0a32c0fc50167873d14f98de5540b7235 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 21:19:32 -0700 Subject: [PATCH 48/57] run all tests --- src/test/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index 6f437a75c7db..defee3a510e4 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -20,8 +20,8 @@ testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: true, // colored output from test results timeout: 25000, - grep: 'Unit Tests', - // invert + grep: 'Multiroot', + invert }); module.exports = testRunner; From 85c1085b334a1fd2ba01ac4f213d55b27b80cfaf Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 21:32:24 -0700 Subject: [PATCH 49/57] changes not to set paths for shebang tests --- src/test/providers/shebangCodeLenseProvider.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/providers/shebangCodeLenseProvider.test.ts b/src/test/providers/shebangCodeLenseProvider.test.ts index 13b47123956c..ffc6090e5221 100644 --- a/src/test/providers/shebangCodeLenseProvider.test.ts +++ b/src/test/providers/shebangCodeLenseProvider.test.ts @@ -25,8 +25,8 @@ suite('Shebang detection', () => { test('A code lens will appear when sheban python and python in settings are different', async () => { const pythonPath = 'someUnknownInterpreter'; - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebang); + PythonSettings.getInstance(editor.document.uri).pythonPath = pythonPath; const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 1, 'No CodeLens available'); @@ -38,8 +38,8 @@ suite('Shebang detection', () => { test('Code lens will not appear when sheban python and python in settings are the same', async () => { PythonSettings.dispose(); const pythonPath = await getFullyQualifiedPathToInterpreter(PythonSettings.getInstance().pythonPath); - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebang); + PythonSettings.getInstance(editor.document.uri).pythonPath = pythonPath; const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); @@ -53,8 +53,8 @@ suite('Shebang detection', () => { if (!IS_WINDOWS) { test('A code lens will appear when shebang python uses env and python settings are different', async () => { - await updateSetting('pythonPath', 'p1', rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebangEnv); + PythonSettings.getInstance(editor.document.uri).pythonPath = 'p1'; const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 1, 'No CodeLens available'); @@ -66,8 +66,8 @@ suite('Shebang detection', () => { test('Code lens will not appear even when shebang python uses env and python settings are the same', async () => { const pythonPath = await getFullyQualifiedPathToInterpreter('python'); - await updateSetting('pythonPath', pythonPath, rootWorkspaceUri, ConfigurationTarget.Workspace); const editor = await openFile(fileShebangEnv); + PythonSettings.getInstance(editor.document.uri).pythonPath = pythonPath; const codeLenses = await setupCodeLens(editor); assert.equal(codeLenses.length, 0, 'CodeLens available although interpreters are equal'); }); From 83e82e14afbbc7c87a14f1a378c4a4dddc05463a Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 21:45:10 -0700 Subject: [PATCH 50/57] remove comments --- src/test/initialize.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/initialize.ts b/src/test/initialize.ts index c5194221a7ea..91721a2b44f5 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -28,18 +28,13 @@ export async function initialize(): Promise { }); } export async function initializeTest(): Promise { - console.log('1'); await initializePython(); - console.log('2'); await closeActiveWindows(); - console.log('3'); if (!configSettings) { configSettings = await require('../client/common/configSettings'); } - console.log('4'); // Dispose any cached python settings (used only in test env) configSettings.PythonSettings.dispose(); - console.log('5'); } export async function wait(timeoutMilliseconds: number) { From 5101eb5ba8b7971be4a38b52d106fb9ebe9174bf Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 21:53:44 -0700 Subject: [PATCH 51/57] update settings only if necessary --- src/test/common.ts | 7 +++++++ src/test/workspaceSymbols/standard.test.ts | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/test/common.ts b/src/test/common.ts index 93be892924ce..360339262c4a 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -15,6 +15,13 @@ export type PythonSettingKeys = 'workspaceSymbols.enabled' | 'pythonPath' | export async function updateSetting(setting: PythonSettingKeys, value: any, resource: Uri, configTarget: ConfigurationTarget) { let settings = workspace.getConfiguration('python', resource); + const currentValue = settings.inspect(setting); + if ((configTarget === ConfigurationTarget.Global && currentValue && currentValue.globalValue === value) || + (configTarget === ConfigurationTarget.Workspace && currentValue && currentValue.workspaceValue === value) || + (configTarget === ConfigurationTarget.WorkspaceFolder && currentValue && currentValue.workspaceFolderValue === value)) { + PythonSettings.dispose(); + return; + } await settings.update(setting, value, configTarget); PythonSettings.dispose(); } diff --git a/src/test/workspaceSymbols/standard.test.ts b/src/test/workspaceSymbols/standard.test.ts index c170f15ef964..b43adcb31263 100644 --- a/src/test/workspaceSymbols/standard.test.ts +++ b/src/test/workspaceSymbols/standard.test.ts @@ -19,12 +19,12 @@ suite('Workspace Symbols', () => { await updateSetting('workspaceSymbols.enabled', false, Uri.file(path.join(symbolFilesPath, 'file.py')), ConfigurationTarget.Workspace); }); - test(`symbols should be returned when enabeld and vice versa`, async () => { + test(`symbols should be returned when enabled and vice versa`, async () => { const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); const outputChannel = new MockOutputChannel('Output'); await updateSetting('workspaceSymbols.enabled', false, workspaceUri, ConfigurationTarget.Workspace); - + let generator = new Generator(workspaceUri, outputChannel); let provider = new WorkspaceSymbolProvider([generator], outputChannel); let symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); From 50e875ca2535f02a5689d446952e7ad587fceb1d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 12 Oct 2017 22:20:20 -0700 Subject: [PATCH 52/57] fix test --- src/test/providers/shebangCodeLenseProvider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/providers/shebangCodeLenseProvider.test.ts b/src/test/providers/shebangCodeLenseProvider.test.ts index ffc6090e5221..c8e914f40afd 100644 --- a/src/test/providers/shebangCodeLenseProvider.test.ts +++ b/src/test/providers/shebangCodeLenseProvider.test.ts @@ -37,7 +37,7 @@ suite('Shebang detection', () => { test('Code lens will not appear when sheban python and python in settings are the same', async () => { PythonSettings.dispose(); - const pythonPath = await getFullyQualifiedPathToInterpreter(PythonSettings.getInstance().pythonPath); + const pythonPath = await getFullyQualifiedPathToInterpreter('python'); const editor = await openFile(fileShebang); PythonSettings.getInstance(editor.document.uri).pythonPath = pythonPath; const codeLenses = await setupCodeLens(editor); From 223b6dc8864ca3037512101b24604b3a3dfb7014 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 13 Oct 2017 08:28:02 -0700 Subject: [PATCH 53/57] include files for tests --- .gitignore | 3 +- src/test/.vscode/settings.json | 12 + src/test/.vscode/tags | 721 +++++++++++++++++++++++++++++++++ 3 files changed, 734 insertions(+), 2 deletions(-) create mode 100644 src/test/.vscode/settings.json create mode 100644 src/test/.vscode/tags diff --git a/.gitignore b/.gitignore index cb3999278277..058d4e514ff2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,7 @@ out node_modules *.pyc -.vscode/.ropeproject/** -src/test/.vscode/** +**/.vscode/.ropeproject/** **/testFiles/**/.cache/** *.noseids .vscode-test diff --git a/src/test/.vscode/settings.json b/src/test/.vscode/settings.json new file mode 100644 index 000000000000..bfbbba081622 --- /dev/null +++ b/src/test/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "python.pythonPath": "python", + "python.linting.pylintEnabled": true, + "python.linting.flake8Enabled": true, + "python.workspaceSymbols.enabled": true, + "python.unitTest.nosetestArgs": [], + "python.unitTest.pyTestArgs": [], + "python.unitTest.unittestArgs": [ + "-s=./tests", + "-p=test_*.py" + ] +} \ No newline at end of file diff --git a/src/test/.vscode/tags b/src/test/.vscode/tags new file mode 100644 index 000000000000..c4371e74af04 --- /dev/null +++ b/src/test/.vscode/tags @@ -0,0 +1,721 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 0.0.0 /f9e6e3c1/ +A ..\\pythonFiles\\autocomp\\pep526.py /^class A:$/;" kind:class line:13 +A ..\\pythonFiles\\definition\\await.test.py /^class A:$/;" kind:class line:3 +B ..\\pythonFiles\\autocomp\\pep526.py /^class B:$/;" kind:class line:17 +B ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^class B(Exception):$/;" kind:class line:19 +B ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^class B(Exception):$/;" kind:class line:19 +B ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^class B(Exception):$/;" kind:class line:19 +BaseRefactoring ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class BaseRefactoring(object):$/;" kind:class line:54 +BoundedQueue ..\\pythonFiles\\autocomp\\misc.py /^ class BoundedQueue(_Verbose):$/;" kind:class line:1250 +BoundedSemaphore ..\\pythonFiles\\autocomp\\misc.py /^def BoundedSemaphore(*args, **kwargs):$/;" kind:function line:497 +C ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^class C(B):$/;" kind:class line:22 +C ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^class C(B):$/;" kind:class line:22 +C ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^class C(B):$/;" kind:class line:22 +Change ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class Change():$/;" kind:class line:41 +ChangeType ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class ChangeType():$/;" kind:class line:32 +Child2Class ..\\pythonFiles\\symbolFiles\\childFile.py /^class Child2Class(object):$/;" kind:class line:5 +Class1 ..\\pythonFiles\\autocomp\\one.py /^class Class1(object):$/;" kind:class line:6 +Class1 ..\\pythonFiles\\definition\\one.py /^class Class1(object):$/;" kind:class line:6 +Condition ..\\pythonFiles\\autocomp\\misc.py /^def Condition(*args, **kwargs):$/;" kind:function line:242 +ConsumerThread ..\\pythonFiles\\autocomp\\misc.py /^ class ConsumerThread(Thread):$/;" kind:class line:1298 +D ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^class D(C):$/;" kind:class line:25 +D ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^class D(C):$/;" kind:class line:25 +D ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^class D(C):$/;" kind:class line:25 +DELETE ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ DELETE = 2$/;" kind:variable line:38 +DELETE ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ DELETE = 2$/;" kind:variable line:46 +Decorator ..\\pythonFiles\\autocomp\\deco.py /^class Decorator(metaclass=abc.ABCMeta):$/;" kind:class line:3 +DoSomething ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^class DoSomething():$/;" kind:class line:200 +DoSomething ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^class DoSomething():$/;" kind:class line:200 +DoSomething ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^class DoSomething():$/;" kind:class line:200 +EDIT ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ EDIT = 0$/;" kind:variable line:36 +EDIT ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ EDIT = 0$/;" kind:variable line:44 +Event ..\\pythonFiles\\autocomp\\misc.py /^def Event(*args, **kwargs):$/;" kind:function line:542 +Example3 ..\\pythonFiles\\formatting\\fileToFormat.py /^class Example3( object ):$/;" kind:class line:12 +ExtractMethodRefactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class ExtractMethodRefactor(ExtractVariableRefactor):$/;" kind:class line:144 +ExtractVariableRefactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class ExtractVariableRefactor(BaseRefactoring):$/;" kind:class line:120 +Foo ..\\multiRootWkspc\\disableLinters\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\multiRootWkspc\\parent\\child\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\multiRootWkspc\\workspace1\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\multiRootWkspc\\workspace2\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\multiRootWkspc\\workspace3\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\pythonFiles\\autocomp\\four.py /^class Foo(object):$/;" kind:class line:7 +Foo ..\\pythonFiles\\definition\\four.py /^class Foo(object):$/;" kind:class line:7 +Foo ..\\pythonFiles\\linting\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\pythonFiles\\linting\\flake8config\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\pythonFiles\\linting\\pep8config\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\pythonFiles\\linting\\pylintconfig\\file.py /^class Foo(object):$/;" kind:class line:5 +Foo ..\\pythonFiles\\symbolFiles\\file.py /^class Foo(object):$/;" kind:class line:5 +Gaussian ..\\pythonFiles\\jupyter\\cells.py /^class Gaussian(object):$/;" kind:class line:100 +Lock ..\\pythonFiles\\autocomp\\misc.py /^Lock = _allocate_lock$/;" kind:variable line:112 +N ..\\pythonFiles\\jupyter\\cells.py /^N = 50$/;" kind:variable line:42 +NEW ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ NEW = 1$/;" kind:variable line:37 +NEW ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ NEW = 1$/;" kind:variable line:45 +PEP_484_style ..\\pythonFiles\\autocomp\\pep526.py /^PEP_484_style = SOMETHING # type: str$/;" kind:variable line:5 +PEP_526_style ..\\pythonFiles\\autocomp\\pep526.py /^PEP_526_style: str = "hello world"$/;" kind:variable line:3 +ProducerThread ..\\pythonFiles\\autocomp\\misc.py /^ class ProducerThread(Thread):$/;" kind:class line:1282 +RLock ..\\pythonFiles\\autocomp\\misc.py /^def RLock(*args, **kwargs):$/;" kind:function line:114 +ROPE_PROJECT_FOLDER ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ROPE_PROJECT_FOLDER = sys.argv[2]$/;" kind:variable line:18 +ROPE_PROJECT_FOLDER ..\\pythonFiles\\sorting\\noconfig\\after.py /^ROPE_PROJECT_FOLDER = sys.argv[2]$/;" kind:variable line:12 +ROPE_PROJECT_FOLDER ..\\pythonFiles\\sorting\\noconfig\\before.py /^ROPE_PROJECT_FOLDER = sys.argv[2]$/;" kind:variable line:9 +ROPE_PROJECT_FOLDER ..\\pythonFiles\\sorting\\noconfig\\original.py /^ROPE_PROJECT_FOLDER = sys.argv[2]$/;" kind:variable line:9 +Random ..\\pythonFiles\\autocomp\\misc.py /^class Random(_random.Random):$/;" kind:class line:1331 +RefactorProgress ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class RefactorProgress():$/;" kind:class line:21 +RenameRefactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class RenameRefactor(BaseRefactoring):$/;" kind:class line:101 +RopeRefactoring ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^class RopeRefactoring(object):$/;" kind:class line:162 +Semaphore ..\\pythonFiles\\autocomp\\misc.py /^def Semaphore(*args, **kwargs):$/;" kind:function line:412 +TOOLS ..\\pythonFiles\\jupyter\\cells.py /^TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select"$/;" kind:variable line:68 +Test_CheckMyApp ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^class Test_CheckMyApp:$/;" kind:class line:6 +Test_CheckMyApp ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^class Test_CheckMyApp:$/;" kind:class line:6 +Test_CheckMyApp ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^class Test_CheckMyApp:$/;" kind:class line:6 +Test_Current_Working_Directory ..\\pythonFiles\\testFiles\\cwd\\src\\tests\\test_cwd.py /^class Test_Current_Working_Directory(unittest.TestCase):$/;" kind:class line:6 +Test_NestedClassA ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ class Test_NestedClassA:$/;" kind:class line:13 +Test_NestedClassA ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ class Test_NestedClassA:$/;" kind:class line:13 +Test_NestedClassA ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ class Test_NestedClassA:$/;" kind:class line:13 +Test_Root_test1 ..\\pythonFiles\\testFiles\\single\\test_root.py /^class Test_Root_test1(unittest.TestCase):$/;" kind:class line:6 +Test_Root_test1 ..\\pythonFiles\\testFiles\\standard\\test_root.py /^class Test_Root_test1(unittest.TestCase):$/;" kind:class line:6 +Test_Root_test1 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\test_root.py /^class Test_Root_test1(unittest.TestCase):$/;" kind:class line:6 +Test_nested_classB_Of_A ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ class Test_nested_classB_Of_A:$/;" kind:class line:16 +Test_nested_classB_Of_A ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ class Test_nested_classB_Of_A:$/;" kind:class line:16 +Test_nested_classB_Of_A ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ class Test_nested_classB_Of_A:$/;" kind:class line:16 +Test_test1 ..\\pythonFiles\\testFiles\\single\\tests\\test_one.py /^class Test_test1(unittest.TestCase):$/;" kind:class line:6 +Test_test1 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_one.py /^class Test_test1(unittest.TestCase):$/;" kind:class line:6 +Test_test1 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_unittest_one.py /^class Test_test1(unittest.TestCase):$/;" kind:class line:6 +Test_test1 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_one.py /^class Test_test1(unittest.TestCase):$/;" kind:class line:6 +Test_test2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^class Test_test2(unittest.TestCase):$/;" kind:class line:3 +Test_test2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^class Test_test2(unittest.TestCase):$/;" kind:class line:3 +Test_test2a ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^class Test_test2a(unittest.TestCase):$/;" kind:class line:17 +Test_test2a ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^class Test_test2a(unittest.TestCase):$/;" kind:class line:17 +Test_test2a1 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ class Test_test2a1(unittest.TestCase):$/;" kind:class line:24 +Test_test2a1 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ class Test_test2a1(unittest.TestCase):$/;" kind:class line:24 +Test_test3 ..\\pythonFiles\\testFiles\\standard\\tests\\unittest_three_test.py /^class Test_test3(unittest.TestCase):$/;" kind:class line:4 +Test_test3 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\unittest_three_test.py /^class Test_test3(unittest.TestCase):$/;" kind:class line:4 +Test_test_one_1 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_one.py /^class Test_test_one_1(unittest.TestCase):$/;" kind:class line:3 +Test_test_one_2 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_one.py /^class Test_test_one_2(unittest.TestCase):$/;" kind:class line:14 +Test_test_two_1 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_two.py /^class Test_test_two_1(unittest.TestCase):$/;" kind:class line:3 +Test_test_two_2 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_two.py /^class Test_test_two_2(unittest.TestCase):$/;" kind:class line:14 +Thread ..\\pythonFiles\\autocomp\\misc.py /^class Thread(_Verbose):$/;" kind:class line:640 +ThreadError ..\\pythonFiles\\autocomp\\misc.py /^ThreadError = thread.error$/;" kind:variable line:38 +Timer ..\\pythonFiles\\autocomp\\misc.py /^def Timer(*args, **kwargs):$/;" kind:function line:1046 +VERSION ..\\pythonFiles\\autocomp\\misc.py /^ VERSION = 3 # used by getstate\/setstate$/;" kind:variable line:1345 +WORKSPACE_ROOT ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^WORKSPACE_ROOT = sys.argv[1]$/;" kind:variable line:17 +WORKSPACE_ROOT ..\\pythonFiles\\sorting\\noconfig\\after.py /^WORKSPACE_ROOT = sys.argv[1]$/;" kind:variable line:11 +WORKSPACE_ROOT ..\\pythonFiles\\sorting\\noconfig\\before.py /^WORKSPACE_ROOT = sys.argv[1]$/;" kind:variable line:8 +WORKSPACE_ROOT ..\\pythonFiles\\sorting\\noconfig\\original.py /^WORKSPACE_ROOT = sys.argv[1]$/;" kind:variable line:8 +Workspace2Class ..\\pythonFiles\\symbolFiles\\workspace2File.py /^class Workspace2Class(object):$/;" kind:class line:5 +_BoundedSemaphore ..\\pythonFiles\\autocomp\\misc.py /^class _BoundedSemaphore(_Semaphore):$/;" kind:class line:515 +_Condition ..\\pythonFiles\\autocomp\\misc.py /^class _Condition(_Verbose):$/;" kind:class line:255 +_DummyThread ..\\pythonFiles\\autocomp\\misc.py /^class _DummyThread(Thread):$/;" kind:class line:1128 +_Event ..\\pythonFiles\\autocomp\\misc.py /^class _Event(_Verbose):$/;" kind:class line:552 +_MainThread ..\\pythonFiles\\autocomp\\misc.py /^class _MainThread(Thread):$/;" kind:class line:1088 +_RLock ..\\pythonFiles\\autocomp\\misc.py /^class _RLock(_Verbose):$/;" kind:class line:125 +_Semaphore ..\\pythonFiles\\autocomp\\misc.py /^class _Semaphore(_Verbose):$/;" kind:class line:423 +_Timer ..\\pythonFiles\\autocomp\\misc.py /^class _Timer(Thread):$/;" kind:class line:1058 +_VERBOSE ..\\pythonFiles\\autocomp\\misc.py /^_VERBOSE = False$/;" kind:variable line:53 +_Verbose ..\\pythonFiles\\autocomp\\misc.py /^ class _Verbose(object):$/;" kind:class line:57 +_Verbose ..\\pythonFiles\\autocomp\\misc.py /^ class _Verbose(object):$/;" kind:class line:79 +__all__ ..\\pythonFiles\\autocomp\\misc.py /^__all__ = ['activeCount', 'active_count', 'Condition', 'currentThread',$/;" kind:variable line:30 +__bootstrap ..\\pythonFiles\\autocomp\\misc.py /^ def __bootstrap(self):$/;" kind:member line:769 +__bootstrap_inner ..\\pythonFiles\\autocomp\\misc.py /^ def __bootstrap_inner(self):$/;" kind:member line:792 +__delete ..\\pythonFiles\\autocomp\\misc.py /^ def __delete(self):$/;" kind:member line:876 +__enter__ ..\\pythonFiles\\autocomp\\misc.py /^ __enter__ = acquire$/;" kind:variable line:185 +__enter__ ..\\pythonFiles\\autocomp\\misc.py /^ __enter__ = acquire$/;" kind:variable line:477 +__enter__ ..\\pythonFiles\\autocomp\\misc.py /^ def __enter__(self):$/;" kind:member line:285 +__exc_clear ..\\pythonFiles\\autocomp\\misc.py /^ __exc_clear = _sys.exc_clear$/;" kind:variable line:654 +__exc_info ..\\pythonFiles\\autocomp\\misc.py /^ __exc_info = _sys.exc_info$/;" kind:variable line:651 +__exit__ ..\\pythonFiles\\autocomp\\misc.py /^ def __exit__(self, *args):$/;" kind:member line:288 +__exit__ ..\\pythonFiles\\autocomp\\misc.py /^ def __exit__(self, t, v, tb):$/;" kind:member line:215 +__exit__ ..\\pythonFiles\\autocomp\\misc.py /^ def __exit__(self, t, v, tb):$/;" kind:member line:493 +__getstate__ ..\\pythonFiles\\autocomp\\misc.py /^ def __getstate__(self): # for pickle$/;" kind:member line:1422 +__init__ ..\\multiRootWkspc\\disableLinters\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\multiRootWkspc\\parent\\child\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\multiRootWkspc\\workspace1\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\multiRootWkspc\\workspace2\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\multiRootWkspc\\workspace3\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, limit):$/;" kind:member line:1252 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, queue, count):$/;" kind:member line:1300 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, queue, quota):$/;" kind:member line:1284 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, verbose=None):$/;" kind:member line:59 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, verbose=None):$/;" kind:member line:80 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self):$/;" kind:member line:1090 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self):$/;" kind:member line:1130 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, group=None, target=None, name=None,$/;" kind:member line:656 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, interval, function, args=[], kwargs={}):$/;" kind:member line:1067 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, lock=None, verbose=None):$/;" kind:member line:260 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, value=1, verbose=None):$/;" kind:member line:433 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, value=1, verbose=None):$/;" kind:member line:521 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, verbose=None):$/;" kind:member line:132 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, verbose=None):$/;" kind:member line:561 +__init__ ..\\pythonFiles\\autocomp\\misc.py /^ def __init__(self, x=None):$/;" kind:member line:1347 +__init__ ..\\pythonFiles\\autocomp\\one.py /^ def __init__(self, file_path=None, file_contents=None):$/;" kind:member line:14 +__init__ ..\\pythonFiles\\definition\\await.test.py /^ def __init__(self):$/;" kind:member line:4 +__init__ ..\\pythonFiles\\definition\\one.py /^ def __init__(self, file_path=None, file_contents=None):$/;" kind:member line:14 +__init__ ..\\pythonFiles\\formatting\\fileToFormat.py /^ def __init__ ( self, bar ):$/;" kind:member line:13 +__init__ ..\\pythonFiles\\jupyter\\cells.py /^ def __init__(self, mean=0.0, std=1, size=1000):$/;" kind:member line:104 +__init__ ..\\pythonFiles\\linting\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\linting\\flake8config\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\linting\\pep8config\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def __init__(self):$/;" kind:member line:164 +__init__ ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def __init__(self, filePath, fileMode=ChangeType.EDIT, diff=""):$/;" kind:member line:48 +__init__ ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def __init__(self, name='Task Name', message=None, percent=0):$/;" kind:member line:26 +__init__ ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def __init__(self, project, resource, name="Extract Method", progressCallback=None, startOff/;" kind:member line:146 +__init__ ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def __init__(self, project, resource, name="Extract Variable", progressCallback=None, startO/;" kind:member line:122 +__init__ ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def __init__(self, project, resource, name="Refactor", progressCallback=None):$/;" kind:member line:59 +__init__ ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def __init__(self, project, resource, name="Rename", progressCallback=None, startOffset=None/;" kind:member line:103 +__init__ ..\\pythonFiles\\symbolFiles\\childFile.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\symbolFiles\\file.py /^ def __init__(self):$/;" kind:member line:8 +__init__ ..\\pythonFiles\\symbolFiles\\workspace2File.py /^ def __init__(self):$/;" kind:member line:8 +__init__.py ..\\pythonFiles\\autoimport\\two\\__init__.py 1;" kind:file line:1 +__initialized ..\\pythonFiles\\autocomp\\misc.py /^ __initialized = False$/;" kind:variable line:646 +__reduce__ ..\\pythonFiles\\autocomp\\misc.py /^ def __reduce__(self):$/;" kind:member line:1428 +__repr__ ..\\pythonFiles\\autocomp\\misc.py /^ def __repr__(self):$/;" kind:member line:138 +__repr__ ..\\pythonFiles\\autocomp\\misc.py /^ def __repr__(self):$/;" kind:member line:291 +__repr__ ..\\pythonFiles\\autocomp\\misc.py /^ def __repr__(self):$/;" kind:member line:713 +__revision__ ..\\multiRootWkspc\\disableLinters\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\multiRootWkspc\\parent\\child\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\multiRootWkspc\\workspace1\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\multiRootWkspc\\workspace2\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\multiRootWkspc\\workspace3\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\linting\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\linting\\flake8config\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\linting\\pep8config\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\linting\\pylintconfig\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\symbolFiles\\childFile.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\symbolFiles\\file.py /^__revision__ = None$/;" kind:variable line:3 +__revision__ ..\\pythonFiles\\symbolFiles\\workspace2File.py /^__revision__ = None$/;" kind:variable line:3 +__setstate__ ..\\pythonFiles\\autocomp\\misc.py /^ def __setstate__(self, state): # for pickle$/;" kind:member line:1425 +__stop ..\\pythonFiles\\autocomp\\misc.py /^ def __stop(self):$/;" kind:member line:866 +_acquire_restore ..\\pythonFiles\\autocomp\\misc.py /^ def _acquire_restore(self, count_owner):$/;" kind:member line:220 +_acquire_restore ..\\pythonFiles\\autocomp\\misc.py /^ def _acquire_restore(self, x):$/;" kind:member line:297 +_active ..\\pythonFiles\\autocomp\\misc.py /^_active = {} # maps thread id to Thread object$/;" kind:variable line:634 +_active_limbo_lock ..\\pythonFiles\\autocomp\\misc.py /^_active_limbo_lock = _allocate_lock()$/;" kind:variable line:633 +_after_fork ..\\pythonFiles\\autocomp\\misc.py /^def _after_fork():$/;" kind:function line:1211 +_allocate_lock ..\\pythonFiles\\autocomp\\misc.py /^_allocate_lock = thread.allocate_lock$/;" kind:variable line:36 +_block ..\\pythonFiles\\autocomp\\misc.py /^ def _block(self):$/;" kind:member line:705 +_count ..\\pythonFiles\\autocomp\\misc.py /^from itertools import count as _count$/;" kind:unknown line:14 +_counter ..\\pythonFiles\\autocomp\\misc.py /^_counter = _count().next$/;" kind:variable line:627 +_deque ..\\pythonFiles\\autocomp\\misc.py /^from collections import deque as _deque$/;" kind:unknown line:13 +_deserialize ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def _deserialize(self, request):$/;" kind:member line:204 +_enumerate ..\\pythonFiles\\autocomp\\misc.py /^def _enumerate():$/;" kind:function line:1179 +_exitfunc ..\\pythonFiles\\autocomp\\misc.py /^ def _exitfunc(self):$/;" kind:member line:1100 +_extractMethod ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def _extractMethod(self, filePath, start, end, newName):$/;" kind:member line:183 +_extractVariable ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def _extractVariable(self, filePath, start, end, newName):$/;" kind:member line:168 +_figure_data ..\\pythonFiles\\jupyter\\cells.py /^ def _figure_data(self, format):$/;" kind:member line:112 +_format_exc ..\\pythonFiles\\autocomp\\misc.py /^from traceback import format_exc as _format_exc$/;" kind:unknown line:16 +_get_ident ..\\pythonFiles\\autocomp\\misc.py /^_get_ident = thread.get_ident$/;" kind:variable line:37 +_is_owned ..\\pythonFiles\\autocomp\\misc.py /^ def _is_owned(self):$/;" kind:member line:238 +_is_owned ..\\pythonFiles\\autocomp\\misc.py /^ def _is_owned(self):$/;" kind:member line:300 +_limbo ..\\pythonFiles\\autocomp\\misc.py /^_limbo = {}$/;" kind:variable line:635 +_newname ..\\pythonFiles\\autocomp\\misc.py /^def _newname(template="Thread-%d"):$/;" kind:function line:629 +_note ..\\pythonFiles\\autocomp\\misc.py /^ def _note(self, *args):$/;" kind:member line:82 +_note ..\\pythonFiles\\autocomp\\misc.py /^ def _note(self, format, *args):$/;" kind:member line:64 +_pickSomeNonDaemonThread ..\\pythonFiles\\autocomp\\misc.py /^def _pickSomeNonDaemonThread():$/;" kind:function line:1113 +_process_request ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def _process_request(self, request):$/;" kind:member line:215 +_profile_hook ..\\pythonFiles\\autocomp\\misc.py /^_profile_hook = None$/;" kind:variable line:87 +_randbelow ..\\pythonFiles\\autocomp\\misc.py /^ def _randbelow(self, n, int=int, maxsize=1< int:$/;" kind:function line:6 +after.py ..\\pythonFiles\\sorting\\noconfig\\after.py 1;" kind:file line:1 +after.py ..\\pythonFiles\\sorting\\withconfig\\after.py 1;" kind:file line:1 +ask_ok ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^ def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):$/;" kind:member line:263 +ask_ok ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):$/;" kind:function line:124 +ask_ok ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^ def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):$/;" kind:member line:263 +ask_ok ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):$/;" kind:function line:124 +ask_ok ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^ def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):$/;" kind:member line:263 +ask_ok ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):$/;" kind:function line:124 +await.test.py ..\\pythonFiles\\definition\\await.test.py 1;" kind:file line:1 +ax ..\\pythonFiles\\jupyter\\cells.py /^fig, ax = plt.subplots(subplot_kw=dict(axisbg='#EEEEEE'))$/;" kind:variable line:39 +b ..\\pythonFiles\\autocomp\\pep526.py /^ b: int = 0$/;" kind:variable line:18 +b ..\\pythonFiles\\typeFormatFiles\\elseBlocksFirstLine2.py /^ b = 3$/;" kind:variable line:3 +b ..\\pythonFiles\\typeFormatFiles\\elseBlocksFirstLine4.py /^ b = 3$/;" kind:variable line:3 +b ..\\pythonFiles\\typeFormatFiles\\elseBlocksFirstLineTab.py /^ b = 3$/;" kind:variable line:3 +bar ..\\pythonFiles\\autocomp\\four.py /^ def bar():$/;" kind:member line:11 +bar ..\\pythonFiles\\definition\\four.py /^ def bar():$/;" kind:member line:11 +before.1.py ..\\pythonFiles\\sorting\\withconfig\\before.1.py 1;" kind:file line:1 +before.py ..\\pythonFiles\\sorting\\noconfig\\before.py 1;" kind:file line:1 +before.py ..\\pythonFiles\\sorting\\withconfig\\before.py 1;" kind:file line:1 +betavariate ..\\pythonFiles\\autocomp\\misc.py /^ def betavariate(self, alpha, beta):$/;" kind:member line:1862 +calculate_cash_flows ..\\pythonFiles\\definition\\decorators.py /^def calculate_cash_flows(remaining_loan_term, remaining_io_term,$/;" kind:function line:20 +cancel ..\\pythonFiles\\autocomp\\misc.py /^ def cancel(self):$/;" kind:member line:1075 +cells.py ..\\pythonFiles\\jupyter\\cells.py 1;" kind:file line:1 +childFile.py ..\\pythonFiles\\symbolFiles\\childFile.py 1;" kind:file line:1 +choice ..\\pythonFiles\\autocomp\\misc.py /^ def choice(self, seq):$/;" kind:member line:1513 +clear ..\\pythonFiles\\autocomp\\misc.py /^ def clear(self):$/;" kind:member line:590 +content ..\\pythonFiles\\autocomp\\doc.py /^ content = line.upper()$/;" kind:variable line:6 +ct ..\\pythonFiles\\autocomp\\two.py /^class ct:$/;" kind:class line:1 +ct ..\\pythonFiles\\definition\\two.py /^class ct:$/;" kind:class line:1 +currentThread ..\\pythonFiles\\autocomp\\misc.py /^def currentThread():$/;" kind:function line:1152 +current_thread ..\\pythonFiles\\autocomp\\misc.py /^current_thread = currentThread$/;" kind:variable line:1165 +daemon ..\\pythonFiles\\autocomp\\misc.py /^ def daemon(self):$/;" kind:member line:1009 +daemon ..\\pythonFiles\\autocomp\\misc.py /^ def daemon(self, daemonic):$/;" kind:member line:1025 +deco.py ..\\pythonFiles\\autocomp\\deco.py 1;" kind:file line:1 +decorators.py ..\\pythonFiles\\definition\\decorators.py 1;" kind:file line:1 +description ..\\pythonFiles\\autocomp\\one.py /^ description = "Run isort on modules registered in setuptools"$/;" kind:variable line:11 +description ..\\pythonFiles\\definition\\one.py /^ description = "Run isort on modules registered in setuptools"$/;" kind:variable line:11 +df ..\\pythonFiles\\jupyter\\cells.py /^df = df.cumsum()$/;" kind:variable line:87 +df ..\\pythonFiles\\jupyter\\cells.py /^df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index,$/;" kind:variable line:85 +divide ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^ def divide(x, y):$/;" kind:member line:329 +divide ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^def divide(x, y):$/;" kind:function line:190 +divide ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^ def divide(x, y):$/;" kind:member line:329 +divide ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^def divide(x, y):$/;" kind:function line:190 +divide ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^ def divide(x, y):$/;" kind:member line:329 +divide ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^def divide(x, y):$/;" kind:function line:190 +divide ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def divide(x, y):$/;" kind:function line:188 +divide ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def divide(x, y):$/;" kind:function line:199 +divide ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def divide(x, y):$/;" kind:function line:188 +divide ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def divide(x, y):$/;" kind:function line:199 +divide ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def divide(x, y):$/;" kind:function line:188 +divide ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def divide(x, y):$/;" kind:function line:199 +doc.py ..\\pythonFiles\\autocomp\\doc.py 1;" kind:file line:1 +dummy.py ..\\pythonFiles\\dummy.py 1;" kind:file line:1 +elseBlocks2.py ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py 1;" kind:file line:1 +elseBlocks4.py ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py 1;" kind:file line:1 +elseBlocksFirstLine2.py ..\\pythonFiles\\typeFormatFiles\\elseBlocksFirstLine2.py 1;" kind:file line:1 +elseBlocksFirstLine4.py ..\\pythonFiles\\typeFormatFiles\\elseBlocksFirstLine4.py 1;" kind:file line:1 +elseBlocksFirstLineTab.py ..\\pythonFiles\\typeFormatFiles\\elseBlocksFirstLineTab.py 1;" kind:file line:1 +elseBlocksTab.py ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py 1;" kind:file line:1 +enumerate ..\\pythonFiles\\autocomp\\misc.py /^def enumerate():$/;" kind:function line:1183 +example1 ..\\pythonFiles\\formatting\\fileToFormat.py /^def example1():$/;" kind:function line:3 +example2 ..\\pythonFiles\\formatting\\fileToFormat.py /^def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key(''));$/;" kind:function line:11 +expovariate ..\\pythonFiles\\autocomp\\misc.py /^ def expovariate(self, lambd):$/;" kind:member line:1670 +fig ..\\pythonFiles\\jupyter\\cells.py /^fig, ax = plt.subplots(subplot_kw=dict(axisbg='#EEEEEE'))$/;" kind:variable line:39 +file.py ..\\multiRootWkspc\\disableLinters\\file.py 1;" kind:file line:1 +file.py ..\\multiRootWkspc\\parent\\child\\file.py 1;" kind:file line:1 +file.py ..\\multiRootWkspc\\workspace1\\file.py 1;" kind:file line:1 +file.py ..\\multiRootWkspc\\workspace2\\file.py 1;" kind:file line:1 +file.py ..\\multiRootWkspc\\workspace3\\file.py 1;" kind:file line:1 +file.py ..\\pythonFiles\\linting\\file.py 1;" kind:file line:1 +file.py ..\\pythonFiles\\linting\\flake8config\\file.py 1;" kind:file line:1 +file.py ..\\pythonFiles\\linting\\pep8config\\file.py 1;" kind:file line:1 +file.py ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py 1;" kind:file line:1 +file.py ..\\pythonFiles\\linting\\pylintconfig\\file.py 1;" kind:file line:1 +file.py ..\\pythonFiles\\symbolFiles\\file.py 1;" kind:file line:1 +fileToFormat.py ..\\pythonFiles\\formatting\\fileToFormat.py 1;" kind:file line:1 +five.py ..\\pythonFiles\\autocomp\\five.py 1;" kind:file line:1 +five.py ..\\pythonFiles\\definition\\five.py 1;" kind:file line:1 +four.py ..\\pythonFiles\\autocomp\\four.py 1;" kind:file line:1 +four.py ..\\pythonFiles\\definition\\four.py 1;" kind:file line:1 +fun ..\\pythonFiles\\autocomp\\two.py /^ def fun():$/;" kind:member line:2 +fun ..\\pythonFiles\\definition\\two.py /^ def fun():$/;" kind:member line:2 +function1 ..\\pythonFiles\\definition\\one.py /^def function1():$/;" kind:function line:33 +function2 ..\\pythonFiles\\definition\\one.py /^def function2():$/;" kind:function line:37 +function3 ..\\pythonFiles\\definition\\one.py /^def function3():$/;" kind:function line:40 +function4 ..\\pythonFiles\\definition\\one.py /^def function4():$/;" kind:function line:43 +gammavariate ..\\pythonFiles\\autocomp\\misc.py /^ def gammavariate(self, alpha, beta):$/;" kind:member line:1737 +gauss ..\\pythonFiles\\autocomp\\misc.py /^ def gauss(self, mu, sigma):$/;" kind:member line:1809 +get ..\\pythonFiles\\autocomp\\misc.py /^ def get(self):$/;" kind:member line:1271 +getName ..\\pythonFiles\\autocomp\\misc.py /^ def getName(self):$/;" kind:member line:1038 +getstate ..\\pythonFiles\\autocomp\\misc.py /^ def getstate(self):$/;" kind:member line:1388 +greeting ..\\pythonFiles\\autocomp\\pep484.py /^def greeting(name: str) -> str:$/;" kind:function line:2 +hoverTest.py ..\\pythonFiles\\autocomp\\hoverTest.py 1;" kind:file line:1 +ident ..\\pythonFiles\\autocomp\\misc.py /^ def ident(self):$/;" kind:member line:984 +identity ..\\pythonFiles\\definition\\decorators.py /^def identity(ob):$/;" kind:function line:1 +imp.py ..\\pythonFiles\\autocomp\\imp.py 1;" kind:file line:1 +instant_print ..\\pythonFiles\\autocomp\\lamb.py /^instant_print = lambda x: [print(x), sys.stdout.flush(), sys.stderr.flush()]$/;" kind:function line:1 +isAlive ..\\pythonFiles\\autocomp\\misc.py /^ def isAlive(self):$/;" kind:member line:995 +isDaemon ..\\pythonFiles\\autocomp\\misc.py /^ def isDaemon(self):$/;" kind:member line:1032 +isSet ..\\pythonFiles\\autocomp\\misc.py /^ def isSet(self):$/;" kind:member line:570 +is_alive ..\\pythonFiles\\autocomp\\misc.py /^ is_alive = isAlive$/;" kind:variable line:1006 +is_set ..\\pythonFiles\\autocomp\\misc.py /^ is_set = isSet$/;" kind:variable line:574 +join ..\\pythonFiles\\autocomp\\misc.py /^ def join(self, timeout=None):$/;" kind:member line:1146 +join ..\\pythonFiles\\autocomp\\misc.py /^ def join(self, timeout=None):$/;" kind:member line:911 +lamb.py ..\\pythonFiles\\autocomp\\lamb.py 1;" kind:file line:1 +local ..\\pythonFiles\\autocomp\\misc.py /^ from thread import _local as local$/;" kind:unknown line:1206 +lognormvariate ..\\pythonFiles\\autocomp\\misc.py /^ def lognormvariate(self, mu, sigma):$/;" kind:member line:1658 +meth1 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\pythonFiles\\linting\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth1(self, arg):$/;" kind:member line:11 +meth1OfChild ..\\pythonFiles\\symbolFiles\\childFile.py /^ def meth1OfChild(self, arg):$/;" kind:member line:11 +meth1OfWorkspace2 ..\\pythonFiles\\symbolFiles\\workspace2File.py /^ def meth1OfWorkspace2(self, arg):$/;" kind:member line:11 +meth2 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\pythonFiles\\linting\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth2 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth2(self, arg):$/;" kind:member line:15 +meth3 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\pythonFiles\\linting\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth3 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth3(self):$/;" kind:member line:21 +meth4 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\pythonFiles\\linting\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth4 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth4(self):$/;" kind:member line:28 +meth5 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\pythonFiles\\linting\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth5 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth5(self):$/;" kind:member line:38 +meth6 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\pythonFiles\\linting\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth6 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth6(self):$/;" kind:member line:53 +meth7 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\pythonFiles\\linting\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth7 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth7(self):$/;" kind:member line:68 +meth8 ..\\multiRootWkspc\\disableLinters\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\multiRootWkspc\\parent\\child\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\multiRootWkspc\\workspace1\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\multiRootWkspc\\workspace2\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\multiRootWkspc\\workspace3\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\pythonFiles\\linting\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\pythonFiles\\linting\\flake8config\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\pythonFiles\\linting\\pep8config\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\pythonFiles\\linting\\pydocstyleconfig27\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\pythonFiles\\linting\\pylintconfig\\file.py /^ def meth8(self):$/;" kind:member line:80 +meth8 ..\\pythonFiles\\symbolFiles\\file.py /^ def meth8(self):$/;" kind:member line:80 +method1 ..\\pythonFiles\\autocomp\\one.py /^ def method1(self):$/;" kind:member line:18 +method1 ..\\pythonFiles\\definition\\one.py /^ def method1(self):$/;" kind:member line:18 +method2 ..\\pythonFiles\\autocomp\\one.py /^ def method2(self):$/;" kind:member line:24 +method2 ..\\pythonFiles\\definition\\one.py /^ def method2(self):$/;" kind:member line:24 +minus ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^ def minus():$/;" kind:member line:287 +minus ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^def minus():$/;" kind:function line:148 +minus ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^ def minus():$/;" kind:member line:287 +minus ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^def minus():$/;" kind:function line:148 +minus ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^ def minus():$/;" kind:member line:287 +minus ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^def minus():$/;" kind:function line:148 +minus ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def minus():$/;" kind:function line:100 +minus ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def minus():$/;" kind:function line:91 +minus ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def minus():$/;" kind:function line:100 +minus ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def minus():$/;" kind:function line:91 +minus ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def minus():$/;" kind:function line:100 +minus ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def minus():$/;" kind:function line:91 +misc.py ..\\pythonFiles\\autocomp\\misc.py 1;" kind:file line:1 +mpl ..\\pythonFiles\\jupyter\\cells.py /^import matplotlib as mpl$/;" kind:namespace line:4 +mpl ..\\pythonFiles\\jupyter\\cells.py /^import matplotlib as mpl$/;" kind:namespace line:94 +myfunc ..\\pythonFiles\\definition\\decorators.py /^def myfunc():$/;" kind:function line:5 +name ..\\pythonFiles\\autocomp\\misc.py /^ def name(self):$/;" kind:member line:968 +name ..\\pythonFiles\\autocomp\\misc.py /^ def name(self, name):$/;" kind:member line:979 +non_parametrized_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_another_pytest.py /^def non_parametrized_username(request):$/;" kind:function line:10 +non_parametrized_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^def non_parametrized_username(request):$/;" kind:function line:33 +non_parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^def non_parametrized_username(request):$/;" kind:function line:33 +non_parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_another_pytest.py /^def non_parametrized_username(request):$/;" kind:function line:10 +non_parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^def non_parametrized_username(request):$/;" kind:function line:33 +normalvariate ..\\pythonFiles\\autocomp\\misc.py /^ def normalvariate(self, mu, sigma):$/;" kind:member line:1633 +notify ..\\pythonFiles\\autocomp\\misc.py /^ def notify(self, n=1):$/;" kind:member line:373 +notifyAll ..\\pythonFiles\\autocomp\\misc.py /^ def notifyAll(self):$/;" kind:member line:400 +notify_all ..\\pythonFiles\\autocomp\\misc.py /^ notify_all = notifyAll$/;" kind:variable line:409 +np ..\\pythonFiles\\jupyter\\cells.py /^import numpy as np$/;" kind:namespace line:34 +np ..\\pythonFiles\\jupyter\\cells.py /^import numpy as np$/;" kind:namespace line:5 +np ..\\pythonFiles\\jupyter\\cells.py /^import numpy as np$/;" kind:namespace line:63 +np ..\\pythonFiles\\jupyter\\cells.py /^import numpy as np$/;" kind:namespace line:78 +np ..\\pythonFiles\\jupyter\\cells.py /^import numpy as np$/;" kind:namespace line:97 +obj ..\\pythonFiles\\autocomp\\one.py /^obj = Class1()$/;" kind:variable line:30 +obj ..\\pythonFiles\\definition\\one.py /^obj = Class1()$/;" kind:variable line:30 +onRefactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def onRefactor(self):$/;" kind:member line:109 +onRefactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def onRefactor(self):$/;" kind:member line:131 +onRefactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def onRefactor(self):$/;" kind:member line:149 +onRefactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def onRefactor(self):$/;" kind:member line:94 +one ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def one():$/;" kind:function line:134 +one ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def one():$/;" kind:function line:150 +one ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def one():$/;" kind:function line:134 +one ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def one():$/;" kind:function line:150 +one ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def one():$/;" kind:function line:134 +one ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def one():$/;" kind:function line:150 +one.py ..\\pythonFiles\\autocomp\\one.py 1;" kind:file line:1 +one.py ..\\pythonFiles\\autoimport\\one.py 1;" kind:file line:1 +one.py ..\\pythonFiles\\definition\\one.py 1;" kind:file line:1 +one.py ..\\pythonFiles\\docstrings\\one.py 1;" kind:file line:1 +original.1.py ..\\pythonFiles\\sorting\\withconfig\\original.1.py 1;" kind:file line:1 +original.py ..\\pythonFiles\\sorting\\noconfig\\original.py 1;" kind:file line:1 +original.py ..\\pythonFiles\\sorting\\withconfig\\original.py 1;" kind:file line:1 +p1 ..\\pythonFiles\\jupyter\\cells.py /^p1 = figure(title="Legend Example", tools=TOOLS)$/;" kind:variable line:70 +parametrized_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_another_pytest.py /^def parametrized_username():$/;" kind:function line:6 +parametrized_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^def parametrized_username():$/;" kind:function line:29 +parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^def parametrized_username():$/;" kind:function line:29 +parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_another_pytest.py /^def parametrized_username():$/;" kind:function line:6 +parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^def parametrized_username():$/;" kind:function line:29 +paretovariate ..\\pythonFiles\\autocomp\\misc.py /^ def paretovariate(self, alpha):$/;" kind:member line:1880 +pd ..\\pythonFiles\\jupyter\\cells.py /^import pandas as pd$/;" kind:namespace line:77 +pep484.py ..\\pythonFiles\\autocomp\\pep484.py 1;" kind:file line:1 +pep526.py ..\\pythonFiles\\autocomp\\pep526.py 1;" kind:file line:1 +plain.py ..\\pythonFiles\\shebang\\plain.py 1;" kind:file line:1 +plt ..\\pythonFiles\\jupyter\\cells.py /^from matplotlib import pyplot as plt$/;" kind:unknown line:80 +plt ..\\pythonFiles\\jupyter\\cells.py /^import matplotlib.pyplot as plt$/;" kind:namespace line:3 +plt ..\\pythonFiles\\jupyter\\cells.py /^import matplotlib.pyplot as plt$/;" kind:namespace line:33 +plt ..\\pythonFiles\\jupyter\\cells.py /^import matplotlib.pyplot as plt$/;" kind:namespace line:93 +print_hello ..\\pythonFiles\\hover\\stringFormat.py /^def print_hello(name):$/;" kind:function line:2 +put ..\\pythonFiles\\autocomp\\misc.py /^ def put(self, item):$/;" kind:member line:1260 +randint ..\\pythonFiles\\autocomp\\misc.py /^ def randint(self, a, b):$/;" kind:member line:1477 +randrange ..\\pythonFiles\\autocomp\\misc.py /^ def randrange(self, start, stop=None, step=1, _int=int):$/;" kind:member line:1433 +refactor ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def refactor(self):$/;" kind:member line:87 +refactor.py ..\\pythonFiles\\refactoring\\standAlone\\refactor.py 1;" kind:file line:1 +release ..\\pythonFiles\\autocomp\\misc.py /^ def release(self):$/;" kind:member line:187 +release ..\\pythonFiles\\autocomp\\misc.py /^ def release(self):$/;" kind:member line:479 +release ..\\pythonFiles\\autocomp\\misc.py /^ def release(self):$/;" kind:member line:525 +rnd ..\\pythonFiles\\autocomp\\hoverTest.py /^rnd = random.Random()$/;" kind:variable line:7 +rnd2 ..\\pythonFiles\\autocomp\\hoverTest.py /^rnd2 = misc.Random()$/;" kind:variable line:12 +run ..\\pythonFiles\\autocomp\\misc.py /^ def run(self):$/;" kind:member line:1289 +run ..\\pythonFiles\\autocomp\\misc.py /^ def run(self):$/;" kind:member line:1305 +run ..\\pythonFiles\\autocomp\\misc.py /^ def run(self):$/;" kind:member line:1079 +run ..\\pythonFiles\\autocomp\\misc.py /^ def run(self):$/;" kind:member line:752 +sample ..\\pythonFiles\\autocomp\\misc.py /^ def sample(self, population, k):$/;" kind:member line:1543 +scatter ..\\pythonFiles\\jupyter\\cells.py /^scatter = ax.scatter(np.random.normal(size=N),$/;" kind:variable line:43 +seed ..\\pythonFiles\\autocomp\\misc.py /^ def seed(self, a=None, version=2):$/;" kind:member line:1356 +set ..\\pythonFiles\\autocomp\\misc.py /^ def set(self):$/;" kind:member line:576 +setDaemon ..\\pythonFiles\\autocomp\\misc.py /^ def setDaemon(self, daemonic):$/;" kind:member line:1035 +setName ..\\pythonFiles\\autocomp\\misc.py /^ def setName(self, name):$/;" kind:member line:1041 +setprofile ..\\pythonFiles\\autocomp\\misc.py /^def setprofile(func):$/;" kind:function line:90 +setstate ..\\pythonFiles\\autocomp\\misc.py /^ def setstate(self, state):$/;" kind:member line:1392 +settrace ..\\pythonFiles\\autocomp\\misc.py /^def settrace(func):$/;" kind:function line:100 +shebang.py ..\\pythonFiles\\shebang\\shebang.py 1;" kind:file line:1 +shebangEnv.py ..\\pythonFiles\\shebang\\shebangEnv.py 1;" kind:file line:1 +shebangInvalid.py ..\\pythonFiles\\shebang\\shebangInvalid.py 1;" kind:file line:1 +showMessage ..\\pythonFiles\\autocomp\\four.py /^def showMessage():$/;" kind:function line:19 +showMessage ..\\pythonFiles\\definition\\four.py /^def showMessage():$/;" kind:function line:19 +shuffle ..\\pythonFiles\\autocomp\\misc.py /^ def shuffle(self, x, random=None):$/;" kind:member line:1521 +start ..\\pythonFiles\\autocomp\\misc.py /^ def start(self):$/;" kind:member line:726 +stop ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def stop(self):$/;" kind:member line:84 +stringFormat.py ..\\pythonFiles\\hover\\stringFormat.py 1;" kind:file line:1 +t ..\\pythonFiles\\autocomp\\hoverTest.py /^t = misc.Thread()$/;" kind:variable line:15 +test ..\\pythonFiles\\definition\\await.test.py /^ async def test(self):$/;" kind:member line:7 +test ..\\pythonFiles\\sorting\\noconfig\\after.py /^def test():$/;" kind:function line:15 +test ..\\pythonFiles\\sorting\\noconfig\\before.py /^def test():$/;" kind:function line:12 +test ..\\pythonFiles\\sorting\\noconfig\\original.py /^def test():$/;" kind:function line:12 +test ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^ def test():$/;" kind:member line:201 +test ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^def test():$/;" kind:function line:62 +test ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^ def test():$/;" kind:member line:201 +test ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^def test():$/;" kind:function line:62 +test ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^ def test():$/;" kind:member line:201 +test ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^def test():$/;" kind:function line:62 +test2 ..\\pythonFiles\\definition\\await.test.py /^ async def test2(self):$/;" kind:member line:10 +test_1_1_1 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_one.py /^ def test_1_1_1(self):$/;" kind:member line:4 +test_1_1_1 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_two.py /^ def test_1_1_1(self):$/;" kind:member line:4 +test_1_1_2 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_one.py /^ def test_1_1_2(self):$/;" kind:member line:7 +test_1_1_2 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_two.py /^ def test_1_1_2(self):$/;" kind:member line:7 +test_1_1_3 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_one.py /^ def test_1_1_3(self):$/;" kind:member line:11 +test_1_1_3 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_two.py /^ def test_1_1_3(self):$/;" kind:member line:11 +test_1_2_1 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_one.py /^ def test_1_2_1(self):$/;" kind:member line:15 +test_222A2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_222A2(self):$/;" kind:member line:18 +test_222A2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_222A2(self):$/;" kind:member line:18 +test_222A2wow ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_222A2wow(self):$/;" kind:member line:25 +test_222A2wow ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_222A2wow(self):$/;" kind:member line:25 +test_222B2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_222B2(self):$/;" kind:member line:21 +test_222B2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_222B2(self):$/;" kind:member line:21 +test_222B2wow ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_222B2wow(self):$/;" kind:member line:28 +test_222B2wow ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_222B2wow(self):$/;" kind:member line:28 +test_2_1_1 ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_two.py /^ def test_2_1_1(self):$/;" kind:member line:15 +test_A ..\\pythonFiles\\testFiles\\single\\tests\\test_one.py /^ def test_A(self):$/;" kind:member line:7 +test_A ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_one.py /^ def test_A(self):$/;" kind:member line:7 +test_A ..\\pythonFiles\\testFiles\\standard\\tests\\unittest_three_test.py /^ def test_A(self):$/;" kind:member line:5 +test_A ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_unittest_one.py /^ def test_A(self):$/;" kind:member line:7 +test_A ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_one.py /^ def test_A(self):$/;" kind:member line:7 +test_A ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\unittest_three_test.py /^ def test_A(self):$/;" kind:member line:5 +test_A2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_A2(self):$/;" kind:member line:4 +test_A2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_A2(self):$/;" kind:member line:4 +test_B ..\\pythonFiles\\testFiles\\single\\tests\\test_one.py /^ def test_B(self):$/;" kind:member line:10 +test_B ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_one.py /^ def test_B(self):$/;" kind:member line:10 +test_B ..\\pythonFiles\\testFiles\\standard\\tests\\unittest_three_test.py /^ def test_B(self):$/;" kind:member line:8 +test_B ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_unittest_one.py /^ def test_B(self):$/;" kind:member line:10 +test_B ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_one.py /^ def test_B(self):$/;" kind:member line:10 +test_B ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\unittest_three_test.py /^ def test_B(self):$/;" kind:member line:8 +test_B2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_B2(self):$/;" kind:member line:7 +test_B2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_B2(self):$/;" kind:member line:7 +test_C2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_C2(self):$/;" kind:member line:10 +test_C2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_C2(self):$/;" kind:member line:10 +test_D2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py /^ def test_D2(self):$/;" kind:member line:13 +test_D2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py /^ def test_D2(self):$/;" kind:member line:13 +test_Root_A ..\\pythonFiles\\testFiles\\single\\test_root.py /^ def test_Root_A(self):$/;" kind:member line:7 +test_Root_A ..\\pythonFiles\\testFiles\\standard\\test_root.py /^ def test_Root_A(self):$/;" kind:member line:7 +test_Root_A ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\test_root.py /^ def test_Root_A(self):$/;" kind:member line:7 +test_Root_B ..\\pythonFiles\\testFiles\\single\\test_root.py /^ def test_Root_B(self):$/;" kind:member line:10 +test_Root_B ..\\pythonFiles\\testFiles\\standard\\test_root.py /^ def test_Root_B(self):$/;" kind:member line:10 +test_Root_B ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\test_root.py /^ def test_Root_B(self):$/;" kind:member line:10 +test_Root_c ..\\pythonFiles\\testFiles\\single\\test_root.py /^ def test_Root_c(self):$/;" kind:member line:14 +test_Root_c ..\\pythonFiles\\testFiles\\standard\\test_root.py /^ def test_Root_c(self):$/;" kind:member line:14 +test_Root_c ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\test_root.py /^ def test_Root_c(self):$/;" kind:member line:14 +test_another_pytest.py ..\\pythonFiles\\testFiles\\standard\\tests\\test_another_pytest.py 1;" kind:file line:1 +test_another_pytest.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_another_pytest.py 1;" kind:file line:1 +test_c ..\\pythonFiles\\testFiles\\single\\tests\\test_one.py /^ def test_c(self):$/;" kind:member line:14 +test_c ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_one.py /^ def test_c(self):$/;" kind:member line:14 +test_c ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_unittest_one.py /^ def test_c(self):$/;" kind:member line:14 +test_c ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_one.py /^ def test_c(self):$/;" kind:member line:14 +test_complex_check ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ def test_complex_check(self):$/;" kind:member line:10 +test_complex_check ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ def test_complex_check(self):$/;" kind:member line:10 +test_complex_check ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ def test_complex_check(self):$/;" kind:member line:10 +test_complex_check2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ def test_complex_check2(self):$/;" kind:member line:24 +test_complex_check2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ def test_complex_check2(self):$/;" kind:member line:24 +test_complex_check2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ def test_complex_check2(self):$/;" kind:member line:24 +test_cwd ..\\pythonFiles\\testFiles\\cwd\\src\\tests\\test_cwd.py /^ def test_cwd(self):$/;" kind:member line:7 +test_cwd.py ..\\pythonFiles\\testFiles\\cwd\\src\\tests\\test_cwd.py 1;" kind:file line:1 +test_d ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ def test_d(self):$/;" kind:member line:17 +test_d ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ def test_d(self):$/;" kind:member line:17 +test_d ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ def test_d(self):$/;" kind:member line:17 +test_nested_class_methodB ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ def test_nested_class_methodB(self):$/;" kind:member line:14 +test_nested_class_methodB ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ def test_nested_class_methodB(self):$/;" kind:member line:14 +test_nested_class_methodB ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ def test_nested_class_methodB(self):$/;" kind:member line:14 +test_nested_class_methodC ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ def test_nested_class_methodC(self):$/;" kind:member line:19 +test_nested_class_methodC ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ def test_nested_class_methodC(self):$/;" kind:member line:19 +test_nested_class_methodC ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ def test_nested_class_methodC(self):$/;" kind:member line:19 +test_one.py ..\\pythonFiles\\testFiles\\single\\tests\\test_one.py 1;" kind:file line:1 +test_parametrized_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_another_pytest.py /^def test_parametrized_username(non_parametrized_username):$/;" kind:function line:16 +test_parametrized_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^def test_parametrized_username(non_parametrized_username):$/;" kind:function line:39 +test_parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^def test_parametrized_username(non_parametrized_username):$/;" kind:function line:39 +test_parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_another_pytest.py /^def test_parametrized_username(non_parametrized_username):$/;" kind:function line:16 +test_parametrized_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^def test_parametrized_username(non_parametrized_username):$/;" kind:function line:39 +test_pytest.py ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py 1;" kind:file line:1 +test_pytest.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py 1;" kind:file line:1 +test_pytest.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py 1;" kind:file line:1 +test_root.py ..\\pythonFiles\\testFiles\\single\\test_root.py 1;" kind:file line:1 +test_root.py ..\\pythonFiles\\testFiles\\standard\\test_root.py 1;" kind:file line:1 +test_root.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\test_root.py 1;" kind:file line:1 +test_simple_check ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ def test_simple_check(self):$/;" kind:member line:8 +test_simple_check ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ def test_simple_check(self):$/;" kind:member line:8 +test_simple_check ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ def test_simple_check(self):$/;" kind:member line:8 +test_simple_check2 ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^ def test_simple_check2(self):$/;" kind:member line:22 +test_simple_check2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^ def test_simple_check2(self):$/;" kind:member line:22 +test_simple_check2 ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^ def test_simple_check2(self):$/;" kind:member line:22 +test_unittest_one.py ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_one.py 1;" kind:file line:1 +test_unittest_one.py ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_one.py 1;" kind:file line:1 +test_unittest_one.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_unittest_one.py 1;" kind:file line:1 +test_unittest_one.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_one.py 1;" kind:file line:1 +test_unittest_two.py ..\\pythonFiles\\testFiles\\specificTest\\tests\\test_unittest_two.py 1;" kind:file line:1 +test_unittest_two.py ..\\pythonFiles\\testFiles\\standard\\tests\\test_unittest_two.py 1;" kind:file line:1 +test_unittest_two.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_unittest_two.py 1;" kind:file line:1 +test_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_another_pytest.py /^def test_username(parametrized_username):$/;" kind:function line:13 +test_username ..\\pythonFiles\\testFiles\\standard\\tests\\test_pytest.py /^def test_username(parametrized_username):$/;" kind:function line:36 +test_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\other\\test_pytest.py /^def test_username(parametrized_username):$/;" kind:function line:36 +test_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_another_pytest.py /^def test_username(parametrized_username):$/;" kind:function line:13 +test_username ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\test_pytest.py /^def test_username(parametrized_username):$/;" kind:function line:36 +testthis ..\\pythonFiles\\definition\\await.test.py /^async def testthis():$/;" kind:function line:13 +three.py ..\\pythonFiles\\autocomp\\three.py 1;" kind:file line:1 +three.py ..\\pythonFiles\\autoimport\\two\\three.py 1;" kind:file line:1 +three.py ..\\pythonFiles\\definition\\three.py 1;" kind:file line:1 +triangular ..\\pythonFiles\\autocomp\\misc.py /^ def triangular(self, low=0.0, high=1.0, mode=None):$/;" kind:member line:1611 +tryBlocks2.py ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py 1;" kind:file line:1 +tryBlocks4.py ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py 1;" kind:file line:1 +tryBlocksTab.py ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py 1;" kind:file line:1 +ts ..\\pythonFiles\\jupyter\\cells.py /^ts = pd.Series(np.random.randn(1000),$/;" kind:variable line:82 +ts ..\\pythonFiles\\jupyter\\cells.py /^ts = ts.cumsum()$/;" kind:variable line:84 +two ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^ def two():$/;" kind:member line:308 +two ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^def two():$/;" kind:function line:169 +two ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^ def two():$/;" kind:member line:308 +two ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^def two():$/;" kind:function line:169 +two ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^ def two():$/;" kind:member line:308 +two ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^def two():$/;" kind:function line:169 +two ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def two():$/;" kind:function line:166 +two ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def two():$/;" kind:function line:177 +two ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def two():$/;" kind:function line:166 +two ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def two():$/;" kind:function line:177 +two ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def two():$/;" kind:function line:166 +two ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def two():$/;" kind:function line:177 +two.py ..\\pythonFiles\\autocomp\\two.py 1;" kind:file line:1 +two.py ..\\pythonFiles\\definition\\two.py 1;" kind:file line:1 +uniform ..\\pythonFiles\\autocomp\\misc.py /^ def uniform(self, a, b):$/;" kind:member line:1605 +unittest_three_test.py ..\\pythonFiles\\testFiles\\standard\\tests\\unittest_three_test.py 1;" kind:file line:1 +unittest_three_test.py ..\\pythonFiles\\testFiles\\unitestsWithConfigs\\tests\\unittest_three_test.py 1;" kind:file line:1 +user_options ..\\pythonFiles\\autocomp\\one.py /^ user_options = []$/;" kind:variable line:12 +user_options ..\\pythonFiles\\definition\\one.py /^ user_options = []$/;" kind:variable line:12 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^var = 100$/;" kind:variable line:1 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^var = 100$/;" kind:variable line:15 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^var = 100$/;" kind:variable line:29 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^var = 100$/;" kind:variable line:339 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks2.py /^var = 100$/;" kind:variable line:353 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^ var = 100$/;" kind:variable line:339 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^var = 100$/;" kind:variable line:1 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^var = 100$/;" kind:variable line:15 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocks4.py /^var = 100$/;" kind:variable line:29 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^ var = 100$/;" kind:variable line:339 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^var = 100$/;" kind:variable line:1 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^var = 100$/;" kind:variable line:15 +var ..\\pythonFiles\\typeFormatFiles\\elseBlocksTab.py /^var = 100$/;" kind:variable line:29 +vonmisesvariate ..\\pythonFiles\\autocomp\\misc.py /^ def vonmisesvariate(self, mu, kappa):$/;" kind:member line:1689 +wait ..\\pythonFiles\\autocomp\\misc.py /^ def wait(self, timeout=None):$/;" kind:member line:309 +wait ..\\pythonFiles\\autocomp\\misc.py /^ def wait(self, timeout=None):$/;" kind:member line:603 +watch ..\\pythonFiles\\refactoring\\standAlone\\refactor.py /^ def watch(self):$/;" kind:member line:234 +weibullvariate ..\\pythonFiles\\autocomp\\misc.py /^ def weibullvariate(self, alpha, beta):$/;" kind:member line:1889 +workspace2File.py ..\\pythonFiles\\symbolFiles\\workspace2File.py 1;" kind:file line:1 +x ..\\pythonFiles\\jupyter\\cells.py /^x = Gaussian(2.0, 1.0)$/;" kind:variable line:131 +x ..\\pythonFiles\\jupyter\\cells.py /^x = np.linspace(0, 20, 100)$/;" kind:variable line:7 +x ..\\pythonFiles\\jupyter\\cells.py /^x = np.linspace(0, 4 * np.pi, 100)$/;" kind:variable line:65 +y ..\\pythonFiles\\jupyter\\cells.py /^y = np.sin(x)$/;" kind:variable line:66 +zero ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def zero():$/;" kind:function line:110 +zero ..\\pythonFiles\\typeFormatFiles\\tryBlocks2.py /^def zero():$/;" kind:function line:122 +zero ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def zero():$/;" kind:function line:110 +zero ..\\pythonFiles\\typeFormatFiles\\tryBlocks4.py /^def zero():$/;" kind:function line:122 +zero ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def zero():$/;" kind:function line:110 +zero ..\\pythonFiles\\typeFormatFiles\\tryBlocksTab.py /^def zero():$/;" kind:function line:122 From 67ec41ea216320332d007d1c68019e452e87f96b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 13 Oct 2017 10:10:12 -0700 Subject: [PATCH 54/57] Fixed travis tests for multi root workspace symbols (#1306) * added logging * more logging * yay * fixed * more fixes * fix tests * removed logging * enable all tests * uncommented --- package.json | 2 +- src/test/standardTest.ts | 8 ++++++++ src/test/workspaceSymbols/standard.test.ts | 23 ++++++++++++++++------ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 src/test/standardTest.ts diff --git a/package.json b/package.json index b0901281ecb4..4f65dd8eb20e 100644 --- a/package.json +++ b/package.json @@ -1593,7 +1593,7 @@ "vscode:prepublish": "tsc -p ./ && webpack", "compile": "webpack && tsc -watch -p ./", "postinstall": "node ./node_modules/vscode/bin/install", - "test": "node ./node_modules/vscode/bin/test && node ./out/test/multiRootTest.js" + "test": "node ./out/test/standardTest.js && node ./out/test/multiRootTest.js" }, "dependencies": { "anser": "^1.1.0", diff --git a/src/test/standardTest.ts b/src/test/standardTest.ts new file mode 100644 index 000000000000..ee31d40bb142 --- /dev/null +++ b/src/test/standardTest.ts @@ -0,0 +1,8 @@ +import * as path from 'path'; + +process.env.CODE_TESTS_WORKSPACE = path.join(__dirname, '..', '..', 'src', 'test'); + +function start() { + require('../../node_modules/vscode/bin/test'); +} +start(); diff --git a/src/test/workspaceSymbols/standard.test.ts b/src/test/workspaceSymbols/standard.test.ts index f7e0270d4fd9..f32a9de3efa7 100644 --- a/src/test/workspaceSymbols/standard.test.ts +++ b/src/test/workspaceSymbols/standard.test.ts @@ -8,7 +8,7 @@ import { WorkspaceSymbolProvider } from '../../client/workspaceSymbols/provider' import { enableDisableWorkspaceSymbols } from './common'; import { PythonSettings } from '../../client/common/configSettings'; -const symbolFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'symbolFiles'); +const workspaceUri = Uri.file(path.join(__dirname, '..', '..', '..', 'src', 'test')); suite('Workspace Symbols', () => { suiteSetup(() => initialize()); @@ -16,15 +16,18 @@ suite('Workspace Symbols', () => { setup(() => PythonSettings.dispose()); teardown(async () => { await closeActiveWindows(); - await enableDisableWorkspaceSymbols(Uri.file(path.join(symbolFilesPath, 'file.py')), false, ConfigurationTarget.Workspace); + await enableDisableWorkspaceSymbols(workspaceUri, false, ConfigurationTarget.Workspace); }); test(`symbols should be returned when enabeld and vice versa`, async () => { - const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(workspaceUri, false, ConfigurationTarget.Workspace); + // The workspace will be in the output test folder + // So lets modify the settings so it sees the source test folder + let settings = PythonSettings.getInstance(workspaceUri); + settings.workspaceSymbols.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags') + let generator = new Generator(workspaceUri, outputChannel); let provider = new WorkspaceSymbolProvider([generator], outputChannel); let symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); @@ -33,17 +36,25 @@ suite('Workspace Symbols', () => { await enableDisableWorkspaceSymbols(workspaceUri, true, ConfigurationTarget.Workspace); + // The workspace will be in the output test folder + // So lets modify the settings so it sees the source test folder + settings = PythonSettings.getInstance(workspaceUri); + settings.workspaceSymbols.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags') + generator = new Generator(workspaceUri, outputChannel); provider = new WorkspaceSymbolProvider([generator], outputChannel); symbols = await provider.provideWorkspaceSymbols('', new CancellationTokenSource().token); assert.notEqual(symbols.length, 0, 'Symbols should be returned when workspace symbols are turned on'); }); test(`symbols should be filtered correctly`, async () => { - const workspaceUri = Uri.file(path.join(symbolFilesPath, 'file.py')); const outputChannel = new MockOutputChannel('Output'); - await enableDisableWorkspaceSymbols(workspaceUri, true, ConfigurationTarget.Workspace); + // The workspace will be in the output test folder + // So lets modify the settings so it sees the source test folder + const settings = PythonSettings.getInstance(workspaceUri); + settings.workspaceSymbols.tagFilePath = path.join(workspaceUri.fsPath, '.vscode', 'tags') + const generators = [new Generator(workspaceUri, outputChannel)]; const provider = new WorkspaceSymbolProvider(generators, outputChannel); const symbols = await provider.provideWorkspaceSymbols('meth1Of', new CancellationTokenSource().token); From ee0003c0c701d3778c7cdeaa39ba38accca5ed66 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 13 Oct 2017 11:01:01 -0700 Subject: [PATCH 55/57] Added brackets around print statements (for p3) --- .../multiRootWkspc/parent/child/childFile.py | 2 +- .../workspace2/workspace2File.py | 2 +- src/test/pythonFiles/symbolFiles/childFile.py | 2 +- src/test/pythonFiles/symbolFiles/file.py | 44 +++++++++---------- .../pythonFiles/symbolFiles/workspace2File.py | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/test/multiRootWkspc/parent/child/childFile.py b/src/test/multiRootWkspc/parent/child/childFile.py index 82547b176285..31d6fc7b4a18 100644 --- a/src/test/multiRootWkspc/parent/child/childFile.py +++ b/src/test/multiRootWkspc/parent/child/childFile.py @@ -10,4 +10,4 @@ def __init__(self): def meth1OfChild(self, arg): """this issues a message""" - print self + print (self) diff --git a/src/test/multiRootWkspc/workspace2/workspace2File.py b/src/test/multiRootWkspc/workspace2/workspace2File.py index 2958414e1e17..61aa87c55fed 100644 --- a/src/test/multiRootWkspc/workspace2/workspace2File.py +++ b/src/test/multiRootWkspc/workspace2/workspace2File.py @@ -10,4 +10,4 @@ def __init__(self): def meth1OfWorkspace2(self, arg): """this issues a message""" - print self + print (self) diff --git a/src/test/pythonFiles/symbolFiles/childFile.py b/src/test/pythonFiles/symbolFiles/childFile.py index 82547b176285..31d6fc7b4a18 100644 --- a/src/test/pythonFiles/symbolFiles/childFile.py +++ b/src/test/pythonFiles/symbolFiles/childFile.py @@ -10,4 +10,4 @@ def __init__(self): def meth1OfChild(self, arg): """this issues a message""" - print self + print (self) diff --git a/src/test/pythonFiles/symbolFiles/file.py b/src/test/pythonFiles/symbolFiles/file.py index 439f899e9e22..27509dd2fcd6 100644 --- a/src/test/pythonFiles/symbolFiles/file.py +++ b/src/test/pythonFiles/symbolFiles/file.py @@ -10,78 +10,78 @@ def __init__(self): def meth1(self, arg): """this issues a message""" - print self + print(self) def meth2(self, arg): """and this one not""" # pylint: disable=unused-argument - print self\ - + "foo" + print (self\ + + "foo") def meth3(self): """test one line disabling""" # no error - print self.bla # pylint: disable=no-member + print (self.bla) # pylint: disable=no-member # error - print self.blop + print (self.blop) def meth4(self): """test re-enabling""" # pylint: disable=no-member # no error - print self.bla - print self.blop + print (self.bla) + print (self.blop) # pylint: enable=no-member # error - print self.blip + print (self.blip) def meth5(self): """test IF sub-block re-enabling""" # pylint: disable=no-member # no error - print self.bla + print (self.bla) if self.blop: # pylint: enable=no-member # error - print self.blip + print (self.blip) else: # no error - print self.blip + print (self.blip) # no error - print self.blip + print (self.blip) def meth6(self): """test TRY/EXCEPT sub-block re-enabling""" # pylint: disable=no-member # no error - print self.bla + print (self.bla) try: # pylint: enable=no-member # error - print self.blip + print (self.blip) except UndefinedName: # pylint: disable=undefined-variable # no error - print self.blip + print (self.blip) # no error - print self.blip + print (self.blip) def meth7(self): """test one line block opening disabling""" if self.blop: # pylint: disable=no-member # error - print self.blip + print (self.blip) else: # error - print self.blip + print (self.blip) # error - print self.blip + print (self.blip) def meth8(self): """test late disabling""" # error - print self.blip + print (self.blip) # pylint: disable=no-member # no error - print self.bla - print self.blop + print (self.bla) + print (self.blop) diff --git a/src/test/pythonFiles/symbolFiles/workspace2File.py b/src/test/pythonFiles/symbolFiles/workspace2File.py index 2958414e1e17..61aa87c55fed 100644 --- a/src/test/pythonFiles/symbolFiles/workspace2File.py +++ b/src/test/pythonFiles/symbolFiles/workspace2File.py @@ -10,4 +10,4 @@ def __init__(self): def meth1OfWorkspace2(self, arg): """this issues a message""" - print self + print (self) From f4274c077a10aa10c1aa8e2b568cdbb293b54d5d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 13 Oct 2017 12:48:59 -0700 Subject: [PATCH 56/57] use resource when getting settings --- .../configProviders/simpleProvider.ts | 2 +- .../providers/execInTerminalProvider.ts | 2 +- .../providers/shebangCodeLensProvider.ts | 2 +- src/client/unittests/common/debugLauncher.ts | 2 +- src/test/common.ts | 3 +- src/test/common/configSettings.test.ts | 4 +- src/test/format/extension.format.test.ts | 54 ++++++------- src/test/format/extension.sort.test.ts | 78 ++++++++++--------- src/test/unittests/pytest.test.ts | 3 +- 9 files changed, 74 insertions(+), 76 deletions(-) diff --git a/src/client/debugger/configProviders/simpleProvider.ts b/src/client/debugger/configProviders/simpleProvider.ts index efb069a67b16..391edd782d99 100644 --- a/src/client/debugger/configProviders/simpleProvider.ts +++ b/src/client/debugger/configProviders/simpleProvider.ts @@ -51,7 +51,7 @@ export class SimpleConfigurationProvider implements DebugConfigurationProvider { type: 'python', request: 'launch', stopOnEntry: true, - pythonPath: PythonSettings.getInstance().pythonPath, + pythonPath: PythonSettings.getInstance(workspaceFolder ? Uri.file(workspaceFolder) : undefined).pythonPath, program: defaultProgram, cwd: workspaceFolder, envFile, diff --git a/src/client/providers/execInTerminalProvider.ts b/src/client/providers/execInTerminalProvider.ts index e248e32bc432..0487f7362cf4 100644 --- a/src/client/providers/execInTerminalProvider.ts +++ b/src/client/providers/execInTerminalProvider.ts @@ -33,7 +33,7 @@ function execInTerminal(fileUri?: vscode.Uri) { const terminalShellSettings = vscode.workspace.getConfiguration('terminal.integrated.shell'); const IS_POWERSHELL = /powershell/.test(terminalShellSettings.get('windows')); - let pythonSettings = settings.PythonSettings.getInstance(); + let pythonSettings = settings.PythonSettings.getInstance(fileUri); let filePath: string; let currentPythonPath = pythonSettings.pythonPath; diff --git a/src/client/providers/shebangCodeLensProvider.ts b/src/client/providers/shebangCodeLensProvider.ts index 2be8598a8463..8988d6161644 100644 --- a/src/client/providers/shebangCodeLensProvider.ts +++ b/src/client/providers/shebangCodeLensProvider.ts @@ -15,7 +15,7 @@ export class ShebangCodeLensProvider implements vscode.CodeLensProvider { private async createShebangCodeLens(document: TextDocument) { const shebang = await ShebangCodeLensProvider.detectShebang(document); - if (!shebang || shebang === settings.PythonSettings.getInstance().pythonPath) { + if (!shebang || shebang === settings.PythonSettings.getInstance(document.uri).pythonPath) { return []; } diff --git a/src/client/unittests/common/debugLauncher.ts b/src/client/unittests/common/debugLauncher.ts index 099bfaa5b377..026a46759eff 100644 --- a/src/client/unittests/common/debugLauncher.ts +++ b/src/client/unittests/common/debugLauncher.ts @@ -4,8 +4,8 @@ import { PythonSettings } from '../../common/configSettings'; import { execPythonFile } from './../../common/utils'; import { createDeferred } from './../../common/helpers'; -const pythonSettings = PythonSettings.getInstance(); export function launchDebugger(rootDirectory: string, testArgs: string[], token?: CancellationToken, outChannel?: OutputChannel) { + const pythonSettings = PythonSettings.getInstance(rootDirectory ? Uri.file(rootDirectory) : undefined); const def = createDeferred(); const launchDef = createDeferred(); let outputChannelShown = false; diff --git a/src/test/common.ts b/src/test/common.ts index 360339262c4a..cbab3da30f19 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -10,7 +10,8 @@ export type PythonSettingKeys = 'workspaceSymbols.enabled' | 'pythonPath' | 'linting.enabled' | 'linting.pylintEnabled' | 'linting.flake8Enabled' | 'linting.pep8Enabled' | 'linting.prospectorEnabled' | 'linting.pydocstyleEnabled' | - 'unitTest.nosetestArgs' | 'unitTest.pyTestArgs' | 'unitTest.unittestArgs'; + 'unitTest.nosetestArgs' | 'unitTest.pyTestArgs' | 'unitTest.unittestArgs' | + 'formatting.formatOnSave' | 'formatting.provider' | 'sortImports.args'; export async function updateSetting(setting: PythonSettingKeys, value: any, resource: Uri, configTarget: ConfigurationTarget) { diff --git a/src/test/common/configSettings.test.ts b/src/test/common/configSettings.test.ts index 07cfe597033a..826ff1b9633d 100644 --- a/src/test/common/configSettings.test.ts +++ b/src/test/common/configSettings.test.ts @@ -14,17 +14,17 @@ import { initialize, IS_TRAVIS } from './../initialize'; import { PythonSettings } from '../../client/common/configSettings'; import { SystemVariables } from '../../client/common/systemVariables'; -const pythonSettings = PythonSettings.getInstance(); const workspaceRoot = path.join(__dirname, '..', '..', '..', 'src', 'test'); // Defines a Mocha test suite to group tests of similar kind together suite('Configuration Settings', () => { setup(() => initialize()); - + if (!IS_TRAVIS) { test('Check Values', done => { const systemVariables: SystemVariables = new SystemVariables(workspaceRoot); const pythonConfig = vscode.workspace.getConfiguration('python'); + const pythonSettings = PythonSettings.getInstance(vscode.Uri.file(workspaceRoot)); Object.keys(pythonSettings).forEach(key => { let settingValue = pythonConfig.get(key, 'Not a config'); if (settingValue === 'Not a config') { diff --git a/src/test/format/extension.format.test.ts b/src/test/format/extension.format.test.ts index 8d8cb1fb866a..1f47e8ea247d 100644 --- a/src/test/format/extension.format.test.ts +++ b/src/test/format/extension.format.test.ts @@ -1,3 +1,4 @@ +import { updateSetting } from '../common'; // Note: This example test is leveraging the Mocha test framework. // Please refer to their documentation on https://mochajs.org/ for help. @@ -10,16 +11,14 @@ import * as assert from 'assert'; // as well as import your extension to test it import * as vscode from 'vscode'; import * as path from 'path'; -import * as settings from '../../client/common/configSettings'; import * as fs from 'fs-extra'; import { EOL } from 'os'; +import { PythonSettings } from '../../client/common/configSettings'; import { AutoPep8Formatter } from '../../client/formatters/autoPep8Formatter'; import { initialize, IS_TRAVIS, closeActiveWindows, initializeTest } from '../initialize'; import { YapfFormatter } from '../../client/formatters/yapfFormatter'; import { execPythonFile } from '../../client/common/utils'; -const pythonSettings = settings.PythonSettings.getInstance(); - const ch = vscode.window.createOutputChannel('Tests'); const pythoFilesPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'formatting'); const workspaceRootPath = path.join(__dirname, '..', '..', '..', 'src', 'test'); @@ -47,14 +46,18 @@ suite('Formatting', () => { formattedAutoPep8 = formattedResults[1]; }).then(() => { }); }); - setup(() => initializeTest()); - suiteTeardown(() => { + setup(async () => { + await initializeTest(); + updateSetting('formatting.formatOnSave', false, vscode.Uri.file(pythoFilesPath), vscode.ConfigurationTarget.Workspace) + }); + suiteTeardown(async () => { [autoPep8FileToFormat, autoPep8FileToAutoFormat, yapfFileToFormat, yapfFileToAutoFormat].forEach(file => { if (fs.existsSync(file)) { fs.unlinkSync(file); } }); - return closeActiveWindows(); + await updateSetting('formatting.formatOnSave', false, vscode.Uri.file(pythoFilesPath), vscode.ConfigurationTarget.Workspace) + await closeActiveWindows(); }); teardown(() => closeActiveWindows()); @@ -86,30 +89,23 @@ suite('Formatting', () => { testFormatting(new YapfFormatter(ch), formattedYapf, yapfFileToFormat).then(done, done); }); - function testAutoFormatting(formatter: string, formattedContents: string, fileToFormat: string): PromiseLike { - let textDocument: vscode.TextDocument; - pythonSettings.formatting.formatOnSave = true; - pythonSettings.formatting.provider = formatter; - return vscode.workspace.openTextDocument(fileToFormat).then(document => { - textDocument = document; - return vscode.window.showTextDocument(textDocument); - }).then(editor => { - assert(vscode.window.activeTextEditor, 'No active editor'); - return editor.edit(editBuilder => { - editBuilder.insert(new vscode.Position(0, 0), '#\n'); - }); - }).then(edited => { - return textDocument.save(); - }).then(saved => { - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve(); - }, 5000); - }); - }).then(() => { - const text = textDocument.getText(); - assert.equal(text === formattedContents, true, 'Formatted contents are not the same'); + async function testAutoFormatting(formatter: string, formattedContents: string, fileToFormat: string): Promise { + await updateSetting('formatting.formatOnSave', true, vscode.Uri.file(fileToFormat), vscode.ConfigurationTarget.Workspace); + await updateSetting('formatting.provider', formatter, vscode.Uri.file(fileToFormat), vscode.ConfigurationTarget.Workspace); + const textDocument = await vscode.workspace.openTextDocument(fileToFormat); + const editor = await vscode.window.showTextDocument(textDocument); + assert(vscode.window.activeTextEditor, 'No active editor'); + const edited = await editor.edit(editBuilder => { + editBuilder.insert(new vscode.Position(0, 0), '#\n'); + }); + const saved = await textDocument.save(); + await new Promise((resolve, reject) => { + setTimeout(() => { + resolve(); + }, 5000); }); + const text = textDocument.getText(); + assert.equal(text === formattedContents, true, 'Formatted contents are not the same'); } test('AutoPep8 autoformat on save', done => { testAutoFormatting('autopep8', `#${EOL}` + formattedAutoPep8, autoPep8FileToAutoFormat).then(done, done); diff --git a/src/test/format/extension.sort.test.ts b/src/test/format/extension.sort.test.ts index c8a427a6b177..31ddcf26b1f4 100644 --- a/src/test/format/extension.sort.test.ts +++ b/src/test/format/extension.sort.test.ts @@ -1,3 +1,4 @@ +import { updateSetting } from '../common'; // Note: This example test is leveraging the Mocha test framework. // Please refer to their documentation on https://mochajs.org/ for help. @@ -16,8 +17,6 @@ import { EOL } from 'os'; import { PythonImportSortProvider } from '../../client/providers/importSortProvider'; import { initialize, IS_TRAVIS, closeActiveWindows, initializeTest } from '../initialize'; -const pythonSettings = settings.PythonSettings.getInstance(); - const sortingPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'sorting'); const fileToFormatWithoutConfig = path.join(sortingPath, 'noconfig', 'before.py'); const originalFileToFormatWithoutConfig = path.join(sortingPath, 'noconfig', 'original.py'); @@ -30,18 +29,19 @@ const extensionDir = path.join(__dirname, '..', '..', '..'); suite('Sorting', () => { suiteSetup(() => initialize()); setup(() => initializeTest()); - suiteTeardown(() => { + suiteTeardown(async () => { fs.writeFileSync(fileToFormatWithConfig, fs.readFileSync(originalFileToFormatWithConfig)); fs.writeFileSync(fileToFormatWithConfig1, fs.readFileSync(originalFileToFormatWithConfig1)); fs.writeFileSync(fileToFormatWithoutConfig, fs.readFileSync(originalFileToFormatWithoutConfig)); - return closeActiveWindows(); + await updateSetting('sortImports.args', [], vscode.Uri.file(sortingPath), vscode.ConfigurationTarget.Workspace); + await closeActiveWindows(); }); - setup(() => { - pythonSettings.sortImports.args = []; + setup(async () => { fs.writeFileSync(fileToFormatWithConfig, fs.readFileSync(originalFileToFormatWithConfig)); fs.writeFileSync(fileToFormatWithoutConfig, fs.readFileSync(originalFileToFormatWithoutConfig)); fs.writeFileSync(fileToFormatWithConfig1, fs.readFileSync(originalFileToFormatWithConfig1)); - return closeActiveWindows(); + await updateSetting('sortImports.args', [], vscode.Uri.file(sortingPath), vscode.ConfigurationTarget.Workspace); + await closeActiveWindows(); }); test('Without Config', done => { @@ -119,8 +119,36 @@ suite('Sorting', () => { test('With Changes and Config in Args', done => { let textEditor: vscode.TextEditor; let textDocument: vscode.TextDocument; - pythonSettings.sortImports.args = ['-sp', path.join(sortingPath, 'withconfig')]; - vscode.workspace.openTextDocument(fileToFormatWithConfig).then(document => { + updateSetting('sortImports.args', ['-sp', path.join(sortingPath, 'withconfig')], vscode.Uri.file(sortingPath), vscode.ConfigurationTarget.Workspace) + .then(() => vscode.workspace.openTextDocument(fileToFormatWithConfig)) + .then(document => { + textDocument = document; + return vscode.window.showTextDocument(textDocument); + }).then(editor => { + assert(vscode.window.activeTextEditor, 'No active editor'); + textEditor = editor; + return editor.edit(editor => { + editor.insert(new vscode.Position(0, 0), 'from third_party import lib0' + EOL); + }); + }).then(() => { + const sorter = new PythonImportSortProvider(); + return sorter.sortImports(extensionDir, textDocument); + }).then(edits => { + const newValue = `from third_party import lib2${EOL}from third_party import lib3${EOL}from third_party import lib4${EOL}from third_party import lib5${EOL}from third_party import lib6${EOL}from third_party import lib7${EOL}from third_party import lib8${EOL}from third_party import lib9${EOL}`; + assert.equal(edits.length, 1, 'Incorrect number of edits'); + assert.equal(edits[0].newText, newValue, 'New Value is not the same'); + assert.equal(`${edits[0].range.start.line},${edits[0].range.start.character}`, '1,0', 'Start position is not the same'); + assert.equal(`${edits[0].range.end.line},${edits[0].range.end.character}`, '4,0', 'End position is not the same'); + }).then(done, done); + }); + } + test('With Changes and Config in Args (via Command)', done => { + let textEditor: vscode.TextEditor; + let textDocument: vscode.TextDocument; + let originalContent = ''; + updateSetting('sortImports.args', ['-sp', path.join(sortingPath, 'withconfig')], vscode.Uri.file(sortingPath), vscode.ConfigurationTarget.Workspace) + .then(() => vscode.workspace.openTextDocument(fileToFormatWithConfig)) + .then(document => { textDocument = document; return vscode.window.showTextDocument(textDocument); }).then(editor => { @@ -130,36 +158,10 @@ suite('Sorting', () => { editor.insert(new vscode.Position(0, 0), 'from third_party import lib0' + EOL); }); }).then(() => { - const sorter = new PythonImportSortProvider(); - return sorter.sortImports(extensionDir, textDocument); + originalContent = textDocument.getText(); + return vscode.commands.executeCommand('python.sortImports'); }).then(edits => { - const newValue = `from third_party import lib2${EOL}from third_party import lib3${EOL}from third_party import lib4${EOL}from third_party import lib5${EOL}from third_party import lib6${EOL}from third_party import lib7${EOL}from third_party import lib8${EOL}from third_party import lib9${EOL}`; - assert.equal(edits.length, 1, 'Incorrect number of edits'); - assert.equal(edits[0].newText, newValue, 'New Value is not the same'); - assert.equal(`${edits[0].range.start.line},${edits[0].range.start.character}`, '1,0', 'Start position is not the same'); - assert.equal(`${edits[0].range.end.line},${edits[0].range.end.character}`, '4,0', 'End position is not the same'); + assert.notEqual(originalContent, textDocument.getText(), 'Contents have not changed'); }).then(done, done); - }); - } - test('With Changes and Config in Args (via Command)', done => { - let textEditor: vscode.TextEditor; - let textDocument: vscode.TextDocument; - let originalContent = ''; - pythonSettings.sortImports.args = ['-sp', path.join(sortingPath, 'withconfig')]; - vscode.workspace.openTextDocument(fileToFormatWithConfig).then(document => { - textDocument = document; - return vscode.window.showTextDocument(textDocument); - }).then(editor => { - assert(vscode.window.activeTextEditor, 'No active editor'); - textEditor = editor; - return editor.edit(editor => { - editor.insert(new vscode.Position(0, 0), 'from third_party import lib0' + EOL); - }); - }).then(() => { - originalContent = textDocument.getText(); - return vscode.commands.executeCommand('python.sortImports'); - }).then(edits => { - assert.notEqual(originalContent, textDocument.getText(), 'Contents have not changed'); - }).then(done, done); }); }); diff --git a/src/test/unittests/pytest.test.ts b/src/test/unittests/pytest.test.ts index f3ea7e9d6712..7156c76ecab1 100644 --- a/src/test/unittests/pytest.test.ts +++ b/src/test/unittests/pytest.test.ts @@ -9,7 +9,6 @@ import { TestResultDisplay } from '../../client/unittests/display/main'; import { MockOutputChannel } from './../mockClasses'; import { rootWorkspaceUri, updateSetting } from '../common'; -const pythonSettings = configSettings.PythonSettings.getInstance(); const UNITTEST_TEST_FILES_PATH = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'standard'); const UNITTEST_SINGLE_TEST_FILE_PATH = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'single'); const UNITTEST_TEST_FILES_PATH_WITH_CONFIGS = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'testFiles', 'unitestsWithConfigs'); @@ -78,7 +77,7 @@ suite('Unit Tests (PyTest)', () => { test('Discover Tests (with config)', async () => { - pythonSettings.unitTest.pyTestArgs = []; + await updateSetting('unitTest.pyTestArgs', [], rootWorkspaceUri, configTarget); rootDirectory = UNITTEST_TEST_FILES_PATH_WITH_CONFIGS; createTestManager(); const tests = await testManager.discoverTests(true, true); From 35b4818985cc9208ee623b818ef24c1feb2a549d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 13 Oct 2017 14:34:46 -0700 Subject: [PATCH 57/57] support multiroot in language services --- src/client/extension.ts | 18 +- src/client/jedi/main.ts | 7 +- src/client/jedi/parsers/CompletionParser.ts | 8 +- .../languageServices/jediProxyFactory.ts | 38 + src/client/providers/completionProvider.ts | 18 +- src/client/providers/definitionProvider.ts | 14 +- src/client/providers/hoverProvider.ts | 9 +- src/client/providers/jediProxy.ts | 778 +++++++++--------- .../providers/objectDefinitionProvider.ts | 12 +- src/client/providers/referenceProvider.ts | 9 +- src/client/providers/signatureProvider.ts | 13 +- src/client/providers/symbolProvider.ts | 11 +- src/test/autocomplete/base.test.ts | 5 +- src/test/autocomplete/pep484.test.ts | 5 +- src/test/autocomplete/pep526.test.ts | 5 +- src/test/common/configSettings.test.ts | 5 +- 16 files changed, 488 insertions(+), 467 deletions(-) create mode 100644 src/client/languageServices/jediProxyFactory.ts diff --git a/src/client/extension.ts b/src/client/extension.ts index d4fdafc7e995..6b32a7518df1 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -1,6 +1,7 @@ 'use strict'; import * as vscode from 'vscode'; +import { JediFactory } from './languageServices/jediProxyFactory'; import { PythonCompletionItemProvider } from './providers/completionProvider'; import { PythonHoverProvider } from './providers/hoverProvider'; import { PythonDefinitionProvider } from './providers/definitionProvider'; @@ -68,7 +69,8 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(activateUpdateSparkLibraryProvider()); activateSimplePythonRefactorProvider(context, formatOutChannel); context.subscriptions.push(activateFormatOnSaveProvider(PYTHON, formatOutChannel)); - context.subscriptions.push(activateGoToObjectDefinitionProvider(context)); + const jediFactory = new JediFactory(context.asAbsolutePath('.')); + context.subscriptions.push(...activateGoToObjectDefinitionProvider(jediFactory)); context.subscriptions.push(vscode.commands.registerCommand(Commands.Start_REPL, () => { getPathFromPythonCommand(["-c", "import sys;print(sys.executable)"]).catch(() => { @@ -99,19 +101,19 @@ export async function activate(context: vscode.ExtensionContext) { ] }); + context.subscriptions.push(jediFactory); context.subscriptions.push(vscode.languages.registerRenameProvider(PYTHON, new PythonRenameProvider(formatOutChannel))); - const definitionProvider = new PythonDefinitionProvider(context); - const jediProx = definitionProvider.JediProxy; + const definitionProvider = new PythonDefinitionProvider(jediFactory); context.subscriptions.push(vscode.languages.registerDefinitionProvider(PYTHON, definitionProvider)); - context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(context, jediProx))); - context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(context, jediProx))); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(context, jediProx), '.')); + context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(jediFactory))); + context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(jediFactory))); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(jediFactory), '.')); context.subscriptions.push(vscode.languages.registerCodeLensProvider(PYTHON, new ShebangCodeLensProvider())) - const symbolProvider = new PythonSymbolProvider(context, jediProx); + const symbolProvider = new PythonSymbolProvider(jediFactory); context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(PYTHON, symbolProvider)); if (pythonSettings.devOptions.indexOf('DISABLE_SIGNATURE') === -1) { - context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(context, jediProx), '(', ',')); + context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(jediFactory), '(', ',')); } if (pythonSettings.formatting.provider !== 'none') { const formatProvider = new PythonFormattingEditProvider(context, formatOutChannel); diff --git a/src/client/jedi/main.ts b/src/client/jedi/main.ts index 9d2055f8961d..6586a1e27eb4 100644 --- a/src/client/jedi/main.ts +++ b/src/client/jedi/main.ts @@ -1,10 +1,10 @@ "use strict"; -import { SocketClient } from './socketClient'; -import { SocketServer } from '../common/comms/socketServer'; import * as child_process from 'child_process'; import * as path from 'path'; import * as vscode from 'vscode'; +import { SocketClient } from './socketClient'; +import { SocketServer } from '../common/comms/socketServer'; import { createDeferred, Deferred } from '../common/helpers'; import { PythonSettings } from '../common/configSettings'; import { EventEmitter } from 'events'; @@ -80,7 +80,8 @@ export class ClientAdapter extends EventEmitter { this.startSocketServer().then(port => { const def = createDeferred(); const options = { env: newEnv, cwd: this.rootDir }; - this.process = child_process.spawn(PythonSettings.getInstance().pythonPath, [pyFile, port.toString()], options); + const rootDirUri = this.rootDir ? vscode.Uri.file(this.rootDir) : undefined; + this.process = child_process.spawn(PythonSettings.getInstance(rootDirUri).pythonPath, [pyFile, port.toString()], options); this.process.stdout.setEncoding('utf8'); this.process.stderr.setEncoding('utf8'); diff --git a/src/client/jedi/parsers/CompletionParser.ts b/src/client/jedi/parsers/CompletionParser.ts index 8f7368e5e4d9..eedc47842730 100644 --- a/src/client/jedi/parsers/CompletionParser.ts +++ b/src/client/jedi/parsers/CompletionParser.ts @@ -1,12 +1,10 @@ -import { CompletionItem, SymbolKind, SnippetString } from 'vscode'; import * as proxy from '../../providers/jediProxy'; import { extractSignatureAndDocumentation } from '../../providers/jediHelpers'; import { PythonSettings } from '../../common/configSettings'; - -const pythonSettings = PythonSettings.getInstance(); +import { CompletionItem, SymbolKind, SnippetString, Uri } from 'vscode'; export class CompletionParser { - public static parse(data: proxy.ICompletionResult): CompletionItem[] { + public static parse(data: proxy.ICompletionResult, resource: Uri): CompletionItem[] { if (!data || data.items.length === 0) { return []; } @@ -16,7 +14,7 @@ export class CompletionParser { completionItem.kind = item.type; completionItem.documentation = sigAndDocs[1].length === 0 ? item.description : sigAndDocs[1]; completionItem.detail = sigAndDocs[0].split(/\r?\n/).join(''); - if (pythonSettings.autoComplete.addBrackets === true && + if (PythonSettings.getInstance(resource).autoComplete.addBrackets === true && (item.kind === SymbolKind.Function || item.kind === SymbolKind.Method)) { completionItem.insertText = new SnippetString(item.text).appendText("(").appendTabstop().appendText(")"); } diff --git a/src/client/languageServices/jediProxyFactory.ts b/src/client/languageServices/jediProxyFactory.ts new file mode 100644 index 000000000000..9af0b012d648 --- /dev/null +++ b/src/client/languageServices/jediProxyFactory.ts @@ -0,0 +1,38 @@ +import { Disposable, Uri, workspace } from 'vscode'; +import { JediProxy, JediProxyHandler, ICommandResult } from '../providers/jediProxy'; + +export class JediFactory implements Disposable { + private disposables: Disposable[]; + private jediProxyHandlers: Map>; + + constructor(private extensionRootPath: string) { + this.disposables = []; + this.jediProxyHandlers = new Map>(); + } + + public dispose() { + this.disposables.forEach(disposable => disposable.dispose()); + this.disposables = []; + } + public getJediProxyHandler(resource: Uri): JediProxyHandler { + const workspaceFolder = workspace.getWorkspaceFolder(resource); + let workspacePath = workspaceFolder ? workspaceFolder.uri.fsPath : undefined; + if (!workspacePath) { + if (Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) { + workspacePath = workspace.workspaceFolders[0].uri.fsPath; + } + else { + workspacePath = __dirname; + } + } + + if (!this.jediProxyHandlers.has(workspacePath)) { + const jediProxy = new JediProxy(this.extensionRootPath, workspacePath); + const jediProxyHandler = new JediProxyHandler(jediProxy); + this.disposables.push(jediProxy, jediProxyHandler); + this.jediProxyHandlers.set(workspacePath, jediProxyHandler); + } + // tslint:disable-next-line:no-non-null-assertion + return this.jediProxyHandlers.get(workspacePath)! as JediProxyHandler; + } +} diff --git a/src/client/providers/completionProvider.ts b/src/client/providers/completionProvider.ts index 7bbafce3f561..a72e45a679bf 100644 --- a/src/client/providers/completionProvider.ts +++ b/src/client/providers/completionProvider.ts @@ -7,17 +7,13 @@ import * as telemetryContracts from '../common/telemetryContracts'; import { extractSignatureAndDocumentation } from './jediHelpers'; import { EOL } from 'os'; import { PythonSettings } from '../common/configSettings'; -import { SnippetString } from 'vscode'; - -const pythonSettings = PythonSettings.getInstance(); +import { SnippetString, Uri } from 'vscode'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonCompletionItemProvider implements vscode.CompletionItemProvider { - private jediProxyHandler: proxy.JediProxyHandler; - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } - private static parseData(data: proxy.ICompletionResult): vscode.CompletionItem[] { + public constructor(private jediFactory: JediFactory) { } + private static parseData(data: proxy.ICompletionResult, resource: Uri): vscode.CompletionItem[] { if (data && data.items.length > 0) { return data.items.map(item => { const sigAndDocs = extractSignatureAndDocumentation(item); @@ -25,7 +21,7 @@ export class PythonCompletionItemProvider implements vscode.CompletionItemProvid completionItem.kind = item.type; completionItem.documentation = sigAndDocs[1].length === 0 ? item.description : sigAndDocs[1]; completionItem.detail = sigAndDocs[0].split(/\r?\n/).join(''); - if (pythonSettings.autoComplete.addBrackets === true && + if (PythonSettings.getInstance(resource).autoComplete.addBrackets === true && (item.kind === vscode.SymbolKind.Function || item.kind === vscode.SymbolKind.Method)) { completionItem.insertText = new SnippetString(item.text).appendText("(").appendTabstop().appendText(")"); } @@ -67,10 +63,10 @@ export class PythonCompletionItemProvider implements vscode.CompletionItemProvid }; const timer = new telemetryHelper.Delays(); - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { timer.stop(); telemetryHelper.sendTelemetryEvent(telemetryContracts.IDE.Completion, {}, timer.toMeasures()); - const completions = PythonCompletionItemProvider.parseData(data); + const completions = PythonCompletionItemProvider.parseData(data, document.uri); return completions; }); } diff --git a/src/client/providers/definitionProvider.ts b/src/client/providers/definitionProvider.ts index f678baaf5351..9fd32b58c222 100644 --- a/src/client/providers/definitionProvider.ts +++ b/src/client/providers/definitionProvider.ts @@ -2,17 +2,11 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; -import * as telemetryContracts from "../common/telemetryContracts"; +import * as telemetryContracts from '../common/telemetryContracts'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonDefinitionProvider implements vscode.DefinitionProvider { - private jediProxyHandler: proxy.JediProxyHandler; - public get JediProxy(): proxy.JediProxy { - return this.jediProxyHandler.JediProxy; - } - - public constructor(context: vscode.ExtensionContext) { - this.jediProxyHandler = new proxy.JediProxyHandler(context); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IDefinitionResult, possibleWord: string): vscode.Definition { if (data && Array.isArray(data.definitions) && data.definitions.length > 0) { const definitions = data.definitions.filter(d => d.text === possibleWord); @@ -46,7 +40,7 @@ export class PythonDefinitionProvider implements vscode.DefinitionProvider { cmd.source = document.getText(); } let possibleWord = document.getText(range); - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonDefinitionProvider.parseData(data, possibleWord); }); } diff --git a/src/client/providers/hoverProvider.ts b/src/client/providers/hoverProvider.ts index b6a1abf70faf..376150f298a8 100644 --- a/src/client/providers/hoverProvider.ts +++ b/src/client/providers/hoverProvider.ts @@ -4,13 +4,10 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; import { highlightCode } from './jediHelpers'; import { EOL } from 'os'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonHoverProvider implements vscode.HoverProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IHoverResult, currentWord: string): vscode.Hover { let results = []; let capturedInfo: string[] = []; @@ -96,7 +93,7 @@ export class PythonHoverProvider implements vscode.HoverProvider { cmd.source = document.getText(); } - const data = await this.jediProxyHandler.sendCommand(cmd, token); + const data = await this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token); if (!data || !data.items.length) { return; } diff --git a/src/client/providers/jediProxy.ts b/src/client/providers/jediProxy.ts index 5b9220dfb223..dfbb5ca0c6cf 100644 --- a/src/client/providers/jediProxy.ts +++ b/src/client/providers/jediProxy.ts @@ -5,15 +5,14 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as settings from './../common/configSettings'; import * as logger from './../common/logger'; -import * as telemetryHelper from "../common/telemetry"; +import * as telemetryHelper from '../common/telemetry'; import { execPythonFile, validatePath } from "../common/utils"; import { createDeferred, Deferred } from '../common/helpers'; import { getCustomEnvVars } from '../common/utils'; import { mergeEnvVariables } from '../common/envFileParser'; +import { IPythonSettings, PythonSettings } from '../common/configSettings'; const IS_WINDOWS = /^win/.test(process.platform); -var proc: child_process.ChildProcess; -var pythonSettings = settings.PythonSettings.getInstance(); const pythonVSCodeTypeMappings = new Map(); pythonVSCodeTypeMappings.set('none', vscode.CompletionItemKind.Value); @@ -122,202 +121,250 @@ commandNames.set(CommandType.Hover, "tooltip"); commandNames.set(CommandType.Usages, "usages"); commandNames.set(CommandType.Symbols, "names"); -export class JediProxy extends vscode.Disposable { - public constructor(context: vscode.ExtensionContext) { - super(killProcess); - - context.subscriptions.push(this); - initialize(context.asAbsolutePath(".")); +export class JediProxy implements vscode.Disposable { + private proc: child_process.ChildProcess; + private pythonSettings: PythonSettings; + + public constructor(private extensionRootDir: string, private workspacePath: string) { + this.pythonSettings = PythonSettings.getInstance(vscode.Uri.file(workspacePath)); + this.lastKnownPythonInterpreter = this.pythonSettings.pythonPath + this.pythonSettings.on('change', this.onPythonSettingsChanged.bind(this)); + vscode.workspace.onDidChangeConfiguration(this.onConfigChanged.bind(this)); + this.onConfigChanged(); + this.initialize(extensionRootDir); + } + public dispose() { + this.killProcess(); } - private cmdId: number = 0; public getNextCommandId(): number { return this.cmdId++; } - public sendCommand(cmd: ICommand): Promise { - return sendCommand(cmd); - } -} -// keep track of the directory so we can re-spawn the process -let pythonProcessCWD = ""; -function initialize(dir: string) { - pythonProcessCWD = dir; - spawnProcess(path.join(dir, "pythonFiles")); -} + // keep track of the directory so we can re-spawn the process + private pythonProcessCWD = ""; + private initialize(dir: string) { + this.pythonProcessCWD = dir; + this.spawnProcess(path.join(dir, "pythonFiles")); + } -// Check if settings changes -let lastKnownPythonInterpreter = pythonSettings.pythonPath; -pythonSettings.on('change', onPythonSettingsChanged); + // Check if settings changes + private lastKnownPythonInterpreter: string; + private onPythonSettingsChanged() { + if (this.lastKnownPythonInterpreter === this.pythonSettings.pythonPath) { + return; + } + this.killProcess(); + this.clearPendingRequests(); + this.initialize(this.pythonProcessCWD); + } -function onPythonSettingsChanged() { - if (lastKnownPythonInterpreter === pythonSettings.pythonPath) { - return; + private clearPendingRequests() { + this.commandQueue = []; + this.commands.forEach(item => { + item.deferred.resolve(); + }); + this.commands.clear(); } - killProcess(); - clearPendingRequests(); - initialize(pythonProcessCWD); -} + private previousData = ""; + private commands = new Map>(); + private commandQueue: number[] = []; -function clearPendingRequests() { - commandQueue = []; - commands.forEach(item => { - item.deferred.resolve(); - }); - commands.clear(); -} -var previousData = ""; -var commands = new Map>(); -var commandQueue: number[] = []; - -function killProcess() { - try { - if (proc) { - proc.kill(); + private killProcess() { + try { + if (this.proc) { + this.proc.kill(); + } } + catch (ex) { } + this.proc = null; } - catch (ex) { } - proc = null; -} -function handleError(source: string, errorMessage: string) { - logger.error(source + ' jediProxy', `Error (${source}) ${errorMessage}`); -} + private handleError(source: string, errorMessage: string) { + logger.error(source + ' jediProxy', `Error (${source}) ${errorMessage}`); + } -let spawnRetryAttempts = 0; -function spawnProcess(dir: string) { - try { - let environmentVariables = { 'PYTHONUNBUFFERED': '1' }; - let customEnvironmentVars = getCustomEnvVars(); - if (customEnvironmentVars) { - environmentVariables = mergeEnvVariables(environmentVariables, customEnvironmentVars); - } - environmentVariables = mergeEnvVariables(environmentVariables); - - logger.log('child_process.spawn in jediProxy', 'Value of pythonSettings.pythonPath is :' + pythonSettings.pythonPath); - const args = ["completion.py"]; - if (typeof pythonSettings.jediPath !== 'string' || pythonSettings.jediPath.length === 0) { - if (Array.isArray(pythonSettings.devOptions) && - pythonSettings.devOptions.some(item => item.toUpperCase().trim() === 'USERELEASEAUTOCOMP')) { - // Use standard version of jedi library - args.push('std'); + private spawnRetryAttempts = 0; + private spawnProcess(dir: string) { + try { + let environmentVariables = { 'PYTHONUNBUFFERED': '1' }; + let customEnvironmentVars = getCustomEnvVars(); + if (customEnvironmentVars) { + environmentVariables = mergeEnvVariables(environmentVariables, customEnvironmentVars); + } + environmentVariables = mergeEnvVariables(environmentVariables); + + logger.log('child_process.spawn in jediProxy', 'Value of pythonSettings.pythonPath is :' + this.pythonSettings.pythonPath); + const args = ["completion.py"]; + if (typeof this.pythonSettings.jediPath !== 'string' || this.pythonSettings.jediPath.length === 0) { + if (Array.isArray(this.pythonSettings.devOptions) && + this.pythonSettings.devOptions.some(item => item.toUpperCase().trim() === 'USERELEASEAUTOCOMP')) { + // Use standard version of jedi library + args.push('std'); + } + else { + // Use preview version of jedi library + args.push('preview'); + } } else { - // Use preview version of jedi library - args.push('preview'); + args.push('custom'); + args.push(this.pythonSettings.jediPath); } + if (Array.isArray(this.pythonSettings.autoComplete.preloadModules) && + this.pythonSettings.autoComplete.preloadModules.length > 0) { + var modules = this.pythonSettings.autoComplete.preloadModules.filter(m => m.trim().length > 0).join(','); + args.push(modules); + } + this.proc = child_process.spawn(this.pythonSettings.pythonPath, args, { + cwd: dir, + env: environmentVariables + }); } - else { - args.push('custom'); - args.push(pythonSettings.jediPath); - } - if (Array.isArray(pythonSettings.autoComplete.preloadModules) && - pythonSettings.autoComplete.preloadModules.length > 0) { - var modules = pythonSettings.autoComplete.preloadModules.filter(m => m.trim().length > 0).join(','); - args.push(modules); + catch (ex) { + return this.handleError("spawnProcess", ex.message); } - proc = child_process.spawn(pythonSettings.pythonPath, args, { - cwd: dir, - env: environmentVariables + this.proc.stderr.setEncoding('utf8'); + this.proc.stderr.on("data", (data: string) => { + this.handleError("stderr", data); }); - } - catch (ex) { - return handleError("spawnProcess", ex.message); - } - proc.stderr.setEncoding('utf8'); - proc.stderr.on("data", (data: string) => { - handleError("stderr", data); - }); - proc.on("end", (end) => { - logger.error('spawnProcess.end', "End - " + end); - }); - proc.on("error", error => { - handleError("error", error + ''); - spawnRetryAttempts++; - if (spawnRetryAttempts < 10 && error && error.message && - error.message.indexOf('This socket has been ended by the other party') >= 0) { - spawnProcess(dir); - } - }); - proc.stdout.setEncoding('utf8'); - proc.stdout.on("data", (data: string) => { - //Possible there was an exception in parsing the data returned - //So append the data then parse it - var dataStr = previousData = previousData + data + ""; - var responses: any[]; - try { - responses = dataStr.split(/\r?\n/g).filter(line => line.length > 0).map(resp => JSON.parse(resp)); - previousData = ""; - } - catch (ex) { - // Possible we've only received part of the data, hence don't clear previousData - // Don't log errors when we haven't received the entire response - if (ex.message.indexOf('Unexpected end of input') === -1 && - ex.message.indexOf('Unexpected end of JSON input') === -1 && - ex.message.indexOf('Unexpected token') === -1) { - handleError("stdout", ex.message); + this.proc.on("end", (end) => { + logger.error('spawnProcess.end', "End - " + end); + }); + this.proc.on("error", error => { + this.handleError("error", error + ''); + this.spawnRetryAttempts++; + if (this.spawnRetryAttempts < 10 && error && error.message && + error.message.indexOf('This socket has been ended by the other party') >= 0) { + this.spawnProcess(dir); } - return; - } - - responses.forEach((response) => { - // What's this, can't remember, - // Great example of poorly written code (this whole file is a mess) - // I think this needs to be removed, because this is misspelt, it is argments, 'U' is missing - // And that case is handled further down - // case CommandType.Arguments: { - // Rewrite this mess to use stratergy.. - if (response["argments"]) { - var index = commandQueue.indexOf(cmd.id); - commandQueue.splice(index, 1); - return; + }); + this.proc.stdout.setEncoding('utf8'); + this.proc.stdout.on("data", (data: string) => { + //Possible there was an exception in parsing the data returned + //So append the data then parse it + var dataStr = this.previousData = this.previousData + data + ""; + var responses: any[]; + try { + responses = dataStr.split(/\r?\n/g).filter(line => line.length > 0).map(resp => JSON.parse(resp)); + this.previousData = ""; } - var responseId = response["id"]; - - var cmd = >commands.get(responseId); - if (typeof cmd === "object" && cmd !== null) { - commands.delete(responseId); - var index = commandQueue.indexOf(cmd.id); - commandQueue.splice(index, 1); - - if (cmd.delays && typeof cmd.telemetryEvent === 'string') { - // cmd.delays.stop(); - // telemetryHelper.sendTelemetryEvent(cmd.telemetryEvent, null, cmd.delays.toMeasures()); + catch (ex) { + // Possible we've only received part of the data, hence don't clear previousData + // Don't log errors when we haven't received the entire response + if (ex.message.indexOf('Unexpected end of input') === -1 && + ex.message.indexOf('Unexpected end of JSON input') === -1 && + ex.message.indexOf('Unexpected token') === -1) { + this.handleError("stdout", ex.message); } + return; + } - // Check if this command has expired - if (cmd.token.isCancellationRequested) { - cmd.deferred.resolve(); + responses.forEach((response) => { + // What's this, can't remember, + // Great example of poorly written code (this whole file is a mess) + // I think this needs to be removed, because this is misspelt, it is argments, 'U' is missing + // And that case is handled further down + // case CommandType.Arguments: { + // Rewrite this mess to use stratergy.. + if (response["argments"]) { + var index = this.commandQueue.indexOf(cmd.id); + this.commandQueue.splice(index, 1); return; } + var responseId = response["id"]; + + var cmd = >this.commands.get(responseId); + if (typeof cmd === "object" && cmd !== null) { + this.commands.delete(responseId); + var index = this.commandQueue.indexOf(cmd.id); + this.commandQueue.splice(index, 1); + + if (cmd.delays && typeof cmd.telemetryEvent === 'string') { + // cmd.delays.stop(); + // telemetryHelper.sendTelemetryEvent(cmd.telemetryEvent, null, cmd.delays.toMeasures()); + } - switch (cmd.command) { - case CommandType.Completions: { - let results = response['results']; - results = Array.isArray(results) ? results : []; - results.forEach(item => { - const originalType = item.type; - item.type = getMappedVSCodeType(originalType); - item.kind = getMappedVSCodeSymbol(originalType); - item.rawType = getMappedVSCodeType(originalType); - }); - - let completionResult: ICompletionResult = { - items: results, - requestId: cmd.id - }; - cmd.deferred.resolve(completionResult); - break; + // Check if this command has expired + if (cmd.token.isCancellationRequested) { + cmd.deferred.resolve(); + return; } - case CommandType.Definitions: { - let defs = response['results']; - let defResult: IDefinitionResult = { - requestId: cmd.id, - definitions: [] - }; - if (defs.length > 0) { - defResult.definitions = defs.map(def => { + + switch (cmd.command) { + case CommandType.Completions: { + let results = response['results']; + results = Array.isArray(results) ? results : []; + results.forEach(item => { + const originalType = item.type; + item.type = getMappedVSCodeType(originalType); + item.kind = getMappedVSCodeSymbol(originalType); + item.rawType = getMappedVSCodeType(originalType); + }); + + let completionResult: ICompletionResult = { + items: results, + requestId: cmd.id + }; + cmd.deferred.resolve(completionResult); + break; + } + case CommandType.Definitions: { + let defs = response['results']; + let defResult: IDefinitionResult = { + requestId: cmd.id, + definitions: [] + }; + if (defs.length > 0) { + defResult.definitions = defs.map(def => { + const originalType = def.type as string; + return { + fileName: def.fileName, + text: def.text, + rawType: originalType, + type: getMappedVSCodeType(originalType), + kind: getMappedVSCodeSymbol(originalType), + container: def.container, + range: { + startLine: def.range.start_line, + startColumn: def.range.start_column, + endLine: def.range.end_line, + endColumn: def.range.end_column + } + }; + }); + } + + cmd.deferred.resolve(defResult); + break; + } + case CommandType.Hover: { + let defs = response['results']; + var defResult: IHoverResult = { + requestId: cmd.id, + items: defs.map(def => { + return { + kind: getMappedVSCodeSymbol(def.type), + description: def.description, + signature: def.signature, + docstring: def.docstring, + text: def.text + }; + }) + }; + + cmd.deferred.resolve(defResult); + break; + } + case CommandType.Symbols: { + let defs = response['results']; + defs = Array.isArray(defs) ? defs : []; + var defResults: ISymbolResult = { + requestId: cmd.id, + definitions: [] + }; + defResults.definitions = defs.map(def => { const originalType = def.type as string; return { fileName: def.fileName, @@ -334,250 +381,201 @@ function spawnProcess(dir: string) { } }; }); - } - cmd.deferred.resolve(defResult); - break; - } - case CommandType.Hover: { - let defs = response['results']; - var defResult: IHoverResult = { - requestId: cmd.id, - items: defs.map(def => { - return { - kind: getMappedVSCodeSymbol(def.type), - description: def.description, - signature: def.signature, - docstring: def.docstring, - text: def.text - }; - }) - }; - - cmd.deferred.resolve(defResult); - break; - } - case CommandType.Symbols: { - let defs = response['results']; - defs = Array.isArray(defs) ? defs : []; - var defResults: ISymbolResult = { - requestId: cmd.id, - definitions: [] - }; - defResults.definitions = defs.map(def => { - const originalType = def.type as string; - return { - fileName: def.fileName, - text: def.text, - rawType: originalType, - type: getMappedVSCodeType(originalType), - kind: getMappedVSCodeSymbol(originalType), - container: def.container, - range: { - startLine: def.range.start_line, - startColumn: def.range.start_column, - endLine: def.range.end_line, - endColumn: def.range.end_column + cmd.deferred.resolve(defResults); + break; + } + case CommandType.Usages: { + let defs = response['results']; + defs = Array.isArray(defs) ? defs : []; + var refResult: IReferenceResult = { + requestId: cmd.id, + references: defs.map(item => { + return { + columnIndex: item.column, + fileName: item.fileName, + lineIndex: item.line - 1, + moduleName: item.moduleName, + name: item.name + }; } + ) }; - }); - - cmd.deferred.resolve(defResults); - break; - } - case CommandType.Usages: { - let defs = response['results']; - defs = Array.isArray(defs) ? defs : []; - var refResult: IReferenceResult = { - requestId: cmd.id, - references: defs.map(item => { - return { - columnIndex: item.column, - fileName: item.fileName, - lineIndex: item.line - 1, - moduleName: item.moduleName, - name: item.name - }; - } - ) - }; - cmd.deferred.resolve(refResult); - break; - } - case CommandType.Arguments: { - let defs = response["results"]; - cmd.deferred.resolve({ - requestId: cmd.id, - definitions: defs - }); - break; + cmd.deferred.resolve(refResult); + break; + } + case CommandType.Arguments: { + let defs = response["results"]; + cmd.deferred.resolve({ + requestId: cmd.id, + definitions: defs + }); + break; + } } } - } - //Ok, check if too many pending requets - if (commandQueue.length > 10) { - var items = commandQueue.splice(0, commandQueue.length - 10); - items.forEach(id => { - if (commands.has(id)) { - const cmd = commands.get(id); - try { - cmd.deferred.resolve(null); - } - catch (ex) { + //Ok, check if too many pending requets + if (this.commandQueue.length > 10) { + var items = this.commandQueue.splice(0, this.commandQueue.length - 10); + items.forEach(id => { + if (this.commands.has(id)) { + const cmd = this.commands.get(id); + try { + cmd.deferred.resolve(null); + } + catch (ex) { + } + this.commands.delete(id); } - commands.delete(id); - } - }); - } + }); + } + }); }); - }); -} - -function sendCommand(cmd: ICommand): Promise { - if (!proc) { - return Promise.reject(new Error("Python proc not initialized")); } - var executionCmd = >cmd; - var payload = createPayload(executionCmd); - executionCmd.deferred = createDeferred(); - // if (typeof executionCmd.telemetryEvent === 'string') { - // executionCmd.delays = new telemetryHelper.Delays(); - // } - try { - proc.stdin.write(JSON.stringify(payload) + "\n"); - commands.set(executionCmd.id, executionCmd); - commandQueue.push(executionCmd.id); - } - catch (ex) { - console.error(ex); - //If 'This socket is closed.' that means process didn't start at all (at least not properly) - if (ex.message === "This socket is closed.") { - killProcess(); + public sendCommand(cmd: ICommand): Promise { + if (!this.proc) { + return Promise.reject(new Error("Python proc not initialized")); } - else { - handleError("sendCommand", ex.message); + var executionCmd = >cmd; + var payload = this.createPayload(executionCmd); + executionCmd.deferred = createDeferred(); + // if (typeof executionCmd.telemetryEvent === 'string') { + // executionCmd.delays = new telemetryHelper.Delays(); + // } + try { + this.proc.stdin.write(JSON.stringify(payload) + "\n"); + this.commands.set(executionCmd.id, executionCmd); + this.commandQueue.push(executionCmd.id); } - return Promise.reject(ex); - } - return executionCmd.deferred.promise; -} - -function createPayload(cmd: IExecutionCommand): any { - var payload = { - id: cmd.id, - prefix: "", - lookup: commandNames.get(cmd.command), - path: cmd.fileName, - source: cmd.source, - line: cmd.lineIndex, - column: cmd.columnIndex, - config: getConfig() - }; - - if (cmd.command === CommandType.Symbols) { - delete payload.column; - delete payload.line; - } - - return payload; -} + catch (ex) { + console.error(ex); + //If 'This socket is closed.' that means process didn't start at all (at least not properly) + if (ex.message === "This socket is closed.") { -let lastKnownPythonPath: string = null; -let additionalAutoCopletePaths: string[] = []; -function getPathFromPythonCommand(args: string[]): Promise { - return execPythonFile(pythonSettings.pythonPath, args, vscode.workspace.rootPath).then(stdout => { - if (stdout.length === 0) { - return ""; + this.killProcess(); + } + else { + this.handleError("sendCommand", ex.message); + } + return Promise.reject(ex); } - let lines = stdout.split(/\r?\n/g).filter(line => line.length > 0); - return validatePath(lines[0]); - }).catch(() => { - return ""; - }); -} -vscode.workspace.onDidChangeConfiguration(onConfigChanged); -onConfigChanged(); -function onConfigChanged() { - // We're only interested in changes to the python path - if (lastKnownPythonPath === pythonSettings.pythonPath) { - return; + return executionCmd.deferred.promise; } - lastKnownPythonPath = pythonSettings.pythonPath; - let filePaths = [ - // Sysprefix - getPathFromPythonCommand(["-c", "import sys;print(sys.prefix)"]), - // exeucutable path - getPathFromPythonCommand(["-c", "import sys;print(sys.executable)"]), - // Python specific site packages - getPathFromPythonCommand(["-c", "from distutils.sysconfig import get_python_lib; print(get_python_lib())"]), - // Python global site packages, as a fallback in case user hasn't installed them in custom environment - getPathFromPythonCommand(["-m", "site", "--user-site"]), - ]; - - let PYTHONPATH: string = process.env['PYTHONPATH']; - if (typeof PYTHONPATH !== 'string') { - PYTHONPATH = ''; - } - let customEnvironmentVars = getCustomEnvVars(); - if (customEnvironmentVars && customEnvironmentVars['PYTHONPATH']) { - let PYTHONPATHFromEnvFile = customEnvironmentVars['PYTHONPATH'] as string; - if (!path.isAbsolute(PYTHONPATHFromEnvFile) && typeof vscode.workspace.rootPath === 'string') { - PYTHONPATHFromEnvFile = path.resolve(vscode.workspace.rootPath, PYTHONPATHFromEnvFile); + private createPayload(cmd: IExecutionCommand): any { + var payload = { + id: cmd.id, + prefix: "", + lookup: commandNames.get(cmd.command), + path: cmd.fileName, + source: cmd.source, + line: cmd.lineIndex, + column: cmd.columnIndex, + config: this.getConfig() + }; + + if (cmd.command === CommandType.Symbols) { + delete payload.column; + delete payload.line; } - PYTHONPATH += (PYTHONPATH.length > 0 ? + path.delimiter : '') + PYTHONPATHFromEnvFile; + + return payload; } - if (typeof PYTHONPATH === 'string' && PYTHONPATH.length > 0) { - filePaths.push(Promise.resolve(PYTHONPATH.trim())); + + private lastKnownPythonPath: string = null; + private additionalAutoCopletePaths: string[] = []; + private getPathFromPythonCommand(args: string[]): Promise { + return execPythonFile(this.pythonSettings.pythonPath, args, this.workspacePath).then(stdout => { + if (stdout.length === 0) { + return ""; + } + let lines = stdout.split(/\r?\n/g).filter(line => line.length > 0); + return validatePath(lines[0]); + }).catch(() => { + return ""; + }); } - Promise.all(filePaths).then(paths => { - // Last item return a path, we need only the folder - if (paths[1].length > 0) { - paths[1] = path.dirname(paths[1]); + private onConfigChanged() { + // We're only interested in changes to the python path + if (this.lastKnownPythonPath === this.pythonSettings.pythonPath) { + return; } - // On windows we also need the libs path (second item will return c:\xxx\lib\site-packages) - // This is returned by "from distutils.sysconfig import get_python_lib; print(get_python_lib())" - if (IS_WINDOWS && paths[2].length > 0) { - paths.splice(3, 0, path.join(paths[2], "..")); + this.lastKnownPythonPath = this.pythonSettings.pythonPath; + let filePaths = [ + // Sysprefix + this.getPathFromPythonCommand(["-c", "import sys;print(sys.prefix)"]), + // exeucutable path + this.getPathFromPythonCommand(["-c", "import sys;print(sys.executable)"]), + // Python specific site packages + this.getPathFromPythonCommand(["-c", "from distutils.sysconfig import get_python_lib; print(get_python_lib())"]), + // Python global site packages, as a fallback in case user hasn't installed them in custom environment + this.getPathFromPythonCommand(["-m", "site", "--user-site"]), + ]; + + let PYTHONPATH: string = process.env['PYTHONPATH']; + if (typeof PYTHONPATH !== 'string') { + PYTHONPATH = ''; } - additionalAutoCopletePaths = paths.filter(p => p.length > 0); - }); -} - -function getConfig() { - // Add support for paths relative to workspace - let extraPaths = pythonSettings.autoComplete.extraPaths.map(extraPath => { - if (path.isAbsolute(extraPath)) { - return extraPath; + let customEnvironmentVars = getCustomEnvVars(); + if (customEnvironmentVars && customEnvironmentVars['PYTHONPATH']) { + let PYTHONPATHFromEnvFile = customEnvironmentVars['PYTHONPATH'] as string; + if (!path.isAbsolute(PYTHONPATHFromEnvFile) && this.workspacePath === 'string') { + PYTHONPATHFromEnvFile = path.resolve(this.workspacePath, PYTHONPATHFromEnvFile); + } + PYTHONPATH += (PYTHONPATH.length > 0 ? + path.delimiter : '') + PYTHONPATHFromEnvFile; } - if (typeof vscode.workspace.rootPath !== 'string') { - return ''; + if (typeof PYTHONPATH === 'string' && PYTHONPATH.length > 0) { + filePaths.push(Promise.resolve(PYTHONPATH.trim())); } - return path.join(vscode.workspace.rootPath, extraPath); - }); + Promise.all(filePaths).then(paths => { + // Last item return a path, we need only the folder + if (paths[1].length > 0) { + paths[1] = path.dirname(paths[1]); + } - // Always add workspace path into extra paths - if (typeof vscode.workspace.rootPath === 'string') { - extraPaths.unshift(vscode.workspace.rootPath); + // On windows we also need the libs path (second item will return c:\xxx\lib\site-packages) + // This is returned by "from distutils.sysconfig import get_python_lib; print(get_python_lib())" + if (IS_WINDOWS && paths[2].length > 0) { + paths.splice(3, 0, path.join(paths[2], "..")); + } + this.additionalAutoCopletePaths = paths.filter(p => p.length > 0); + }); } - let distinctExtraPaths = extraPaths.concat(additionalAutoCopletePaths) - .filter(value => value.length > 0) - .filter((value, index, self) => self.indexOf(value) === index); - - return { - extraPaths: distinctExtraPaths, - useSnippets: false, - caseInsensitiveCompletion: true, - showDescriptions: true, - fuzzyMatcher: true - }; -} + private getConfig() { + // Add support for paths relative to workspace + let extraPaths = this.pythonSettings.autoComplete.extraPaths.map(extraPath => { + if (path.isAbsolute(extraPath)) { + return extraPath; + } + if (typeof this.workspacePath !== 'string') { + return ''; + } + return path.join(this.workspacePath, extraPath); + }); + + // Always add workspace path into extra paths + if (typeof this.workspacePath === 'string') { + extraPaths.unshift(this.workspacePath); + } + let distinctExtraPaths = extraPaths.concat(this.additionalAutoCopletePaths) + .filter(value => value.length > 0) + .filter((value, index, self) => self.indexOf(value) === index); + + return { + extraPaths: distinctExtraPaths, + useSnippets: false, + caseInsensitiveCompletion: true, + showDescriptions: true, + fuzzyMatcher: true + }; + } +} export interface ICommand { telemetryEvent?: string; command: CommandType; @@ -675,18 +673,18 @@ export interface IHoverItem { signature: string; } -export class JediProxyHandler { - private jediProxy: JediProxy; +export class JediProxyHandler implements vscode.Disposable { private cancellationTokenSource: vscode.CancellationTokenSource; public get JediProxy(): JediProxy { return this.jediProxy; } - public constructor(context: vscode.ExtensionContext, jediProxy: JediProxy = null) { - this.jediProxy = jediProxy ? jediProxy : new JediProxy(context); + public constructor(private jediProxy: JediProxy = null) { + } + public dispose() { + this.jediProxy.dispose(); } - public sendCommand(cmd: ICommand, token?: vscode.CancellationToken): Promise { var executionCmd = >cmd; executionCmd.id = executionCmd.id || this.jediProxy.getNextCommandId(); diff --git a/src/client/providers/objectDefinitionProvider.ts b/src/client/providers/objectDefinitionProvider.ts index f7ffea73dfb4..b455c4ba1933 100644 --- a/src/client/providers/objectDefinitionProvider.ts +++ b/src/client/providers/objectDefinitionProvider.ts @@ -2,16 +2,18 @@ import * as vscode from 'vscode'; import * as defProvider from './definitionProvider'; +import { JediFactory } from '../languageServices/jediProxyFactory'; -export function activateGoToObjectDefinitionProvider(context: vscode.ExtensionContext): vscode.Disposable { - let def = new PythonObjectDefinitionProvider(context); - return vscode.commands.registerCommand("python.goToPythonObject", () => def.goToObjectDefinition()); +export function activateGoToObjectDefinitionProvider(jediFactory: JediFactory): vscode.Disposable[] { + const def = new PythonObjectDefinitionProvider(jediFactory); + const commandRegistration = vscode.commands.registerCommand("python.goToPythonObject", () => def.goToObjectDefinition()); + return [def, commandRegistration] as vscode.Disposable[]; } export class PythonObjectDefinitionProvider { private readonly _defProvider: defProvider.PythonDefinitionProvider; - public constructor(context: vscode.ExtensionContext) { - this._defProvider = new defProvider.PythonDefinitionProvider(context); + public constructor(jediFactory: JediFactory) { + this._defProvider = new defProvider.PythonDefinitionProvider(jediFactory); } public async goToObjectDefinition() { diff --git a/src/client/providers/referenceProvider.ts b/src/client/providers/referenceProvider.ts index d25eefb8823a..c8f2031cf5ed 100644 --- a/src/client/providers/referenceProvider.ts +++ b/src/client/providers/referenceProvider.ts @@ -2,14 +2,11 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonReferenceProvider implements vscode.ReferenceProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IReferenceResult): vscode.Location[] { if (data && data.references.length > 0) { var references = data.references.filter(ref => { @@ -52,7 +49,7 @@ export class PythonReferenceProvider implements vscode.ReferenceProvider { cmd.source = document.getText(); } - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonReferenceProvider.parseData(data); }); } diff --git a/src/client/providers/signatureProvider.ts b/src/client/providers/signatureProvider.ts index 7d0794444578..5ed411ed47b6 100644 --- a/src/client/providers/signatureProvider.ts +++ b/src/client/providers/signatureProvider.ts @@ -1,8 +1,9 @@ "use strict"; -import * as vscode from "vscode"; +import * as vscode from 'vscode'; +import * as proxy from './jediProxy'; import { TextDocument, Position, CancellationToken, SignatureHelp } from "vscode"; -import * as proxy from "./jediProxy"; +import { JediFactory } from '../languageServices/jediProxyFactory'; const DOCSTRING_PARAM_PATTERNS = [ "\\s*:type\\s*PARAMNAME:\\s*([^\\n, ]+)", // Sphinx @@ -43,11 +44,7 @@ function extractParamDocString(paramName: string, docString: string): string { return paramDocString.trim(); } export class PythonSignatureProvider implements vscode.SignatureHelpProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(data: proxy.IArgumentsResult): vscode.SignatureHelp { if (data && Array.isArray(data.definitions) && data.definitions.length > 0) { let signature = new SignatureHelp(); @@ -86,7 +83,7 @@ export class PythonSignatureProvider implements vscode.SignatureHelpProvider { lineIndex: position.line, source: document.getText() }; - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonSignatureProvider.parseData(data); }); } diff --git a/src/client/providers/symbolProvider.ts b/src/client/providers/symbolProvider.ts index 4e89b27f8426..78181b2a62e8 100644 --- a/src/client/providers/symbolProvider.ts +++ b/src/client/providers/symbolProvider.ts @@ -2,13 +2,10 @@ import * as vscode from 'vscode'; import * as proxy from './jediProxy'; +import { JediFactory } from '../languageServices/jediProxyFactory'; export class PythonSymbolProvider implements vscode.DocumentSymbolProvider { - private jediProxyHandler: proxy.JediProxyHandler; - - public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { - this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy); - } + public constructor(private jediFactory: JediFactory) { } private static parseData(document: vscode.TextDocument, data: proxy.ISymbolResult): vscode.SymbolInformation[] { if (data) { let symbols = data.definitions.filter(sym => sym.fileName === document.fileName); @@ -38,7 +35,7 @@ export class PythonSymbolProvider implements vscode.DocumentSymbolProvider { cmd.source = document.getText(); } - return this.jediProxyHandler.sendCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommand(cmd, token).then(data => { return PythonSymbolProvider.parseData(document, data); }); } @@ -56,7 +53,7 @@ export class PythonSymbolProvider implements vscode.DocumentSymbolProvider { cmd.source = document.getText(); } - return this.jediProxyHandler.sendCommandNonCancellableCommand(cmd, token).then(data => { + return this.jediFactory.getJediProxyHandler(document.uri).sendCommandNonCancellableCommand(cmd, token).then(data => { return PythonSymbolProvider.parseData(document, data); }); } diff --git a/src/test/autocomplete/base.test.ts b/src/test/autocomplete/base.test.ts index a1e3feeb9491..ee203a46835f 100644 --- a/src/test/autocomplete/base.test.ts +++ b/src/test/autocomplete/base.test.ts @@ -12,8 +12,9 @@ import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { initialize, closeActiveWindows, initializeTest } from '../initialize'; import { execPythonFile } from '../../client/common/utils'; +import { PythonSettings } from '../../client/common/configSettings'; +import { rootWorkspaceUri } from '../common'; -const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); const fileOne = path.join(autoCompPath, 'one.py'); const fileImport = path.join(autoCompPath, 'imp.py'); @@ -27,7 +28,7 @@ suite('Autocomplete', () => { let isPython3: Promise; suiteSetup(async () => { await initialize(); - let version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); + let version = await execPythonFile(PythonSettings.getInstance(rootWorkspaceUri).pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); setup(() => initializeTest()); diff --git a/src/test/autocomplete/pep484.test.ts b/src/test/autocomplete/pep484.test.ts index 546c26d1253d..1ad220ea929c 100644 --- a/src/test/autocomplete/pep484.test.ts +++ b/src/test/autocomplete/pep484.test.ts @@ -12,8 +12,9 @@ import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { execPythonFile } from '../../client/common/utils'; import { initialize, closeActiveWindows, initializeTest } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { rootWorkspaceUri } from '../common'; -const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); const filePep484 = path.join(autoCompPath, 'pep484.py'); @@ -21,7 +22,7 @@ suite('Autocomplete PEP 484', () => { let isPython3: Promise; suiteSetup(async () => { await initialize(); - const version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); + const version = await execPythonFile(PythonSettings.getInstance(rootWorkspaceUri).pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); setup(() => initializeTest()); diff --git a/src/test/autocomplete/pep526.test.ts b/src/test/autocomplete/pep526.test.ts index b7c4b37de08a..6d330195ccdf 100644 --- a/src/test/autocomplete/pep526.test.ts +++ b/src/test/autocomplete/pep526.test.ts @@ -12,8 +12,9 @@ import * as path from 'path'; import * as settings from '../../client/common/configSettings'; import { execPythonFile } from '../../client/common/utils'; import { initialize, closeActiveWindows, initializeTest } from '../initialize'; +import { PythonSettings } from '../../client/common/configSettings'; +import { rootWorkspaceUri } from '../common'; -const pythonSettings = settings.PythonSettings.getInstance(); const autoCompPath = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonFiles', 'autocomp'); const filePep526 = path.join(autoCompPath, 'pep526.py'); @@ -21,7 +22,7 @@ suite('Autocomplete PEP 526', () => { let isPython3: Promise; suiteSetup(async () => { await initialize(); - const version = await execPythonFile(pythonSettings.pythonPath, ['--version'], __dirname, true); + const version = await execPythonFile(PythonSettings.getInstance(rootWorkspaceUri).pythonPath, ['--version'], __dirname, true); isPython3 = Promise.resolve(version.indexOf('3.') >= 0); }); setup(() => initializeTest()); diff --git a/src/test/common/configSettings.test.ts b/src/test/common/configSettings.test.ts index 07cfe597033a..82d481c8c0da 100644 --- a/src/test/common/configSettings.test.ts +++ b/src/test/common/configSettings.test.ts @@ -13,18 +13,19 @@ import * as path from 'path'; import { initialize, IS_TRAVIS } from './../initialize'; import { PythonSettings } from '../../client/common/configSettings'; import { SystemVariables } from '../../client/common/systemVariables'; +import { rootWorkspaceUri } from '../common'; -const pythonSettings = PythonSettings.getInstance(); const workspaceRoot = path.join(__dirname, '..', '..', '..', 'src', 'test'); // Defines a Mocha test suite to group tests of similar kind together suite('Configuration Settings', () => { setup(() => initialize()); - + if (!IS_TRAVIS) { test('Check Values', done => { const systemVariables: SystemVariables = new SystemVariables(workspaceRoot); const pythonConfig = vscode.workspace.getConfiguration('python'); + const pythonSettings = PythonSettings.getInstance(rootWorkspaceUri); Object.keys(pythonSettings).forEach(key => { let settingValue = pythonConfig.get(key, 'Not a config'); if (settingValue === 'Not a config') {