From d032d3187e6c5fcb278bba5514ce3da6d189b5a3 Mon Sep 17 00:00:00 2001 From: Daniel Biehl Date: Mon, 12 Jul 2021 13:43:10 +0200 Subject: [PATCH] introduce ESLint and Prettier --- .eslintignore | 10 + .eslintrc | 106 + .github/workflows/build.yml | 18 +- .gitignore | 2 + .prettierignore | 18 + .prettierrc | 7 + .vscode/launch.json | 3 +- language-configuration.json | 58 +- package.json | 1038 +++++----- syntaxes/robotframework.tmLanguage.json | 2026 ++++++++++---------- tsconfig.json | 56 +- vscode-client/config.ts | 2 +- vscode-client/debugmanager.ts | 577 +++--- vscode-client/extension.ts | 202 +- vscode-client/languageclientsmanger.ts | 536 +++--- vscode-client/pythonmanger.ts | 161 +- vscode-client/robottestadapter.ts | 77 - vscode-client/test/runTest.ts | 30 +- vscode-client/test/suite/extension.test.ts | 16 +- vscode-client/test/suite/index.ts | 62 +- vscode-client/utils.ts | 69 +- webpack.config.js | 42 +- 22 files changed, 2606 insertions(+), 2510 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .prettierignore create mode 100644 .prettierrc delete mode 100644 vscode-client/robottestadapter.ts diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..07dd61b81 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +out/ +coverage/ + +vscode.d.ts +vscode.proposed.d.ts + +.mypy_cache/ +.pytest_cache/ \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..9a0640b85 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,106 @@ +{ + "env": { + "node": true, + "es6": true, + "mocha": true + }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "prettier"], + "extends": [ + "standard", + "plugin:@typescript-eslint/recommended", + "plugin:import/errors", + "plugin:import/warnings", + "plugin:import/typescript", + "prettier" + ], + "rules": { + // Overriding ESLint rules with Typescript-specific ones + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "ts-ignore": "allow-with-description" + } + ], + "@typescript-eslint/explicit-module-boundary-types": "error", + "no-bitwise": "off", + "no-dupe-class-members": "off", + "@typescript-eslint/no-dupe-class-members": "error", + "no-empty-function": "off", + "@typescript-eslint/no-empty-function": ["error"], + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-non-null-assertion": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "args": "after-used", + "argsIgnorePattern": "^_" + } + ], + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": [ + "error", + { + "functions": false + } + ], + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": "error", + "@typescript-eslint/no-var-requires": "off", + // Other rules + "class-methods-use-this": [ + "error", + { + "exceptMethods": ["dispose"] + } + ], + "func-names": "off", + "import/extensions": "off", + "import/namespace": "off", + "import/no-extraneous-dependencies": "off", + "import/no-unresolved": [ + "error", + { + "ignore": ["monaco-editor", "vscode"] + } + ], + "import/prefer-default-export": "off", + "linebreak-style": "off", + "no-await-in-loop": "off", + "no-console": "off", + "no-control-regex": "off", + "no-extend-native": "off", + "no-multi-str": "off", + "no-param-reassign": "off", + "no-prototype-builtins": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "ForInStatement", + "message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array." + }, + { + "selector": "LabeledStatement", + "message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand." + }, + { + "selector": "WithStatement", + "message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." + } + ], + "no-template-curly-in-string": "off", + "no-underscore-dangle": "off", + "no-useless-escape": "off", + "no-void": [ + "error", + { + "allowAsStatement": true + } + ], + "operator-assignment": "off", + "strict": "off", + "prettier/prettier": ["error"] + } +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e11b1c4bf..f2e510a57 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: [3.8, 3.9] + python-version: [3.9] steps: - uses: actions/checkout@v2 @@ -27,14 +27,14 @@ jobs: with: python-version: ${{ matrix.python-version }} - - uses: Gr1N/setup-poetry@v4 - + - uses: Gr1N/setup-poetry@v4 + - run: pip install poetry-dynamic-versioning - run: poetry install - run: poetry-dynamic-versioning - + - name: "test python packages" run: poetry run pytest --junitxml=test-results/python-${{ matrix.python-version }}/test-results.xml --cov=robotcode --cov-report=xml:testresults/python-${{ matrix.python-version }}/coverage.xml --cov-report=html:test-results/python-${{ matrix.python-version }}/htmlcov @@ -72,10 +72,10 @@ jobs: - name: setup python environment uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.9 + + - uses: Gr1N/setup-poetry@v4 - - uses: Gr1N/setup-poetry@v4 - - run: pip install poetry-dynamic-versioning - run: poetry install @@ -111,14 +111,14 @@ jobs: node-version: ">=15.5" - uses: actions/setup-python@v2 with: - python-version: "3.8" + python-version: "3.9" - uses: Gr1N/setup-poetry@v4 - run: pip install poetry-dynamic-versioning - run: poetry install - + - run: poetry-dynamic-versioning - name: Build Python packages diff --git a/.gitignore b/.gitignore index 76b170681..ff425aefc 100644 --- a/.gitignore +++ b/.gitignore @@ -276,3 +276,5 @@ package-lock.json test-results .python-version +/vscode.d.ts +/vscode.proposed.d.ts \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..4e915e1ca --- /dev/null +++ b/.prettierignore @@ -0,0 +1,18 @@ +# don't ever lint node_modules +node_modules/ +# don't lint build output (make sure it's set to your correct build folder name) +dist/ +out/ +# don't lint nyc coverage output +coverage/ + +package-lock.json +.vscode/ +vscode.d.ts +vscode.proposed.d.ts + +.mypy_cache/ +.pytest_cache/ +robotcode/ + +*.md \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..f2827618f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "tabWidth": 2, + "useTabs": false, + "endOfLine": "auto", + "quoteProps": "as-needed", + "printWidth": 120 +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 9b5673eca..03cc7a402 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -129,7 +129,8 @@ "type": "extensionHost", "request": "launch", "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" + "--extensionDevelopmentPath=${workspaceFolder}", + "--enable-proposed-api" ], "outFiles": [ "${workspaceFolder}/out/**/*.js" diff --git a/language-configuration.json b/language-configuration.json index bb9f3ebb6..4830f535a 100644 --- a/language-configuration.json +++ b/language-configuration.json @@ -1,30 +1,30 @@ { - "comments": { - // symbol used for single line comment. Remove this entry if your language does not support line comments - "lineComment": "#", - }, - // symbols used as brackets - "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] - ], - // symbols that are auto closed when typing - "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] - ], - // symbols that can be used to surround a selection - "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["*", "*"], - ["_", "_"] - ] -} \ No newline at end of file + "comments": { + // symbol used for single line comment. Remove this entry if your language does not support line comments + "lineComment": "#" + }, + // symbols used as brackets + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + // symbols that are auto closed when typing + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + // symbols that can be used to surround a selection + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"], + ["*", "*"], + ["_", "_"] + ] +} diff --git a/package.json b/package.json index 9113a11e5..4d6bf430b 100644 --- a/package.json +++ b/package.json @@ -1,525 +1,543 @@ { - "name": "robotcode", - "displayName": "robotcode", - "description": "Robot Framework support for Visual Studio Code", - "icon": "images/icon.png", - "publisher": "d-biehl", - "version": "0.1.0-alpha.3", - "repository": { - "type": "git", - "url": "https://github.com/d-biehl/robotcode.git" - }, - "engines": { - "vscode": "^1.57.0" - }, - "categories": [ - "Programming Languages", - "Testing", - "Debuggers", - "Formatters", - "Linters" + "name": "robotcode", + "displayName": "robotcode", + "description": "Robot Framework support for Visual Studio Code", + "icon": "images/icon.png", + "publisher": "d-biehl", + "version": "0.1.0-alpha.3", + "enableProposedApi": true, + "repository": { + "type": "git", + "url": "https://github.com/d-biehl/robotcode.git" + }, + "engines": { + "vscode": "^1.58.0" + }, + "categories": [ + "Programming Languages", + "Testing", + "Debuggers", + "Formatters", + "Linters" + ], + "keywords": [ + "Test", + "Testing", + "RobotFramework", + "Robot Framework", + "Robot", + "Keyword Driven" + ], + "activationEvents": [ + "workspaceContains:**/*.robot", + "onDebug", + "onDebugAdapterProtocolTracker:robotframework", + "onDebugInitialConfigurations", + "onDebugDynamicConfigurations", + "onDebugResolve:robotframework", + "onLanguage:robotframework", + "onCommand:robotcode.runTest", + "onCommand:robotcode.debugTest", + "onCommand:robotcode.runSuite", + "onCommand:robotcode.debugSuite" + ], + "main": "./out/extension.js", + "contributes": { + "languages": [ + { + "id": "robotframework", + "aliases": [ + "Robot Framework", + "robotframework" + ], + "extensions": [ + ".robot", + ".resource" + ], + "configuration": "./language-configuration.json" + } ], - "keywords": [ - "Test", - "Testing", - "RobotFramework", - "Robot Framework", - "Robot", - "Keyword Driven" + "grammars": [ + { + "language": "robotframework", + "scopeName": "source.robotframework", + "path": "./syntaxes/robotframework.tmLanguage.json" + } ], - "activationEvents": [ - "workspaceContains:**/*.robot", - "onDebug", - "onDebugAdapterProtocolTracker:robotframework", - "onDebugInitialConfigurations", - "onDebugDynamicConfigurations", - "onDebugResolve:robotframework", - "onLanguage:robotframework", - "onCommand:robotcode.runTest", - "onCommand:robotcode.debugTest", - "onCommand:robotcode.runSuite", - "onCommand:robotcode.debugSuite" + "keybindings": [ + { + "key": "tab", + "command": "type", + "args": { + "text": " " + }, + "when": "editorTextFocus && editorLangId == robotframework && !editorHasSelection && !inSnippetMode && !suggestWidgetVisible && config.robotcode.editor.4SpacesTab" + } ], - "main": "./out/extension.js", - "contributes": { - "languages": [ - { - "id": "robotframework", - "aliases": [ - "Robot Framework", - "robotframework" - ], - "extensions": [ - ".robot", - ".resource" - ], - "configuration": "./language-configuration.json" - } - ], - "grammars": [ - { - "language": "robotframework", - "scopeName": "source.robotframework", - "path": "./syntaxes/robotframework.tmLanguage.json" - } - ], - "keybindings": [ - { - "key": "tab", - "command": "type", - "args": { - "text": " " - }, - "when": "editorTextFocus && editorLangId == robotframework && !editorHasSelection && !inSnippetMode && !suggestWidgetVisible && config.robotcode.editor.4SpacesTab" - } - ], - "configuration": [ - { - "title": "RobotCode", - "type": "object", - "properties": { - "robotcode.python": { - "type": "string", - "default": "", - "description": "Specifies the python executable to be used for RobotCode. If no path is specified, try to get it from \"python\" extension. Requires a VSCode restart to take effect.", - "scope": "resource" - }, - "robotcode.editor.4SpacesTab": { - "type": "boolean", - "default": true, - "description": "If actived insert 4 spaces if TAB is pressed." - }, - "robotcode.languageServer.mode": { - "type": "string", - "default": "stdio", - "description": "Specifies the mode the language server is started. Requires a VSCode restart to take effect.", - "enum": [ - "stdio", - "tcp" - ], - "scope": "resource" - }, - "robotcode.languageServer.tcpPort": { - "type": "number", - "default": 0, - "description": "If the port is specified, connect to the language server previously started at the given port. Requires a VSCode restart to take effect.", - "scope": "resource" - }, - "robotcode.languageServer.args": { - "type": "array", - "default": [], - "items": { - "type": "string" - }, - "description": "Specifies the arguments to be passed to the language server (i.e.: [\"--log\", \"--log-file=~/robotcode.log\"]). Requires a VSCode restart to take effect.", - "scope": "resource" - }, - "robotcode.debugAdapter.mode": { - "type": "string", - "default": "stdio", - "description": "Specifies the mode the debug adapter is started. Requires a VSCode restart to take effect.", - "enum": [ - "stdio", - "tcp" - ], - "scope": "resource" - }, - "robotcode.debugAdapter.tcpPort": { - "type": "number", - "default": 0, - "description": "If the port is specified, connect to the debug adapter previously started at the given port. Requires a VSCode restart to take effect.", - "scope": "resource" - }, - "robotcode.debugAdapter.host": { - "type": "string", - "default": null, - "description": "If the host is specified, connect to the debug adapter previously started at the given host. Requires a VSCode restart to take effect.", - "scope": "resource" - }, - "robotcode.debugAdapter.args": { - "type": "array", - "default": [], - "items": { - "type": "string" - }, - "description": "Specifies the arguments to be passed to the debug adapter (i.e.: [\"--log\", \"--log-file=~/debug-adapter.log\"]). Requires a VSCode restart to take effect.", - "scope": "resource" - }, - "robotcode.robot.args": { - "type": "array", - "default": [ - "--outputdir", - "results" - ], - "items": { - "type": "string" - }, - "description": "Specifies the arguments to be passed to robot module.", - "scope": "resource" - }, - "robotcode.robot.pythonPath": { - "type": "array", - "default": [ - "./lib", - "./resources" - ], - "items": { - "type": "string" - }, - "description": "Specifies additional python paths to robotframework. Corresponds to the '--pythonpath' option of the robot module.", - "scope": "resource" - }, - "robotcode.robot.env": { - "type": "object", - "default": {}, - "description": "Specifies the environment variables for robotframework.", - "scope": "resource" - }, - "robotcode.robot.variables": { - "type": "object", - "default": {}, - "description": "Specifies the variables for robotframework. Corresponds to the '--variable name:value' option of the robot module.", - "scope": "resource" - }, - "robotcode.debug.defaultConfiguration": { - "type": "object", - "default": {}, - "description": "Specifies the default configuration to run or debug robot tests.", - "scope": "resource" - }, - "robotcode.debug.defaultConsole": { - "type": "string", - "enum": [ - "internalConsole", - "integratedTerminal", - "externalTerminal" - ], - "default": "integratedTerminal", - "enumDescriptions": [ - "No terminal (pipes the output to the client debug console).", - "Use terminal integrated in client.", - "External terminal (configured in user settings)." - ], - "description": "Default setting for where to launch the debug target: internal console, integrated terminal, or external terminal.", - "scope": "resource" - }, - "robotcode.robocop.enabled": { - "type": "boolean", - "default": true, - "description": "Enables 'robocop' code analysis, if installed.", - "scope": "resource" - }, - "robotcode.syntax.sectionStyle": { - "type": "string", - "default": "*** {name}s ***", - "description": "Defines the section style format.", - "scope": "resource" - }, - "robotcode.run.openReportAfterRun": { - "type": "string", - "enum": [ - "disabled", - "external" - ], - "enumDescriptions": [ - "Do not open the result report", - "Opens the report in an external browser." - ], - "default": "none", - "description": "Defines if the test report should be opened a run session automatically.", - "scope": "resource" - }, - "robotcode.robocop.include": { - "type": "array", - "default": [], - "items": { - "type": "string" - }, - "description": "Ignore specified 'robocop' rules. You can define rule by its name or id. Glob patterns are supported", - "scope": "resource" - }, - "robotcode.robocop.exclude": { - "type": "array", - "default": [], - "items": { - "type": "string" - }, - "description": "Ignore specified 'robocop' rules. You can define rule by its name or id. Glob patterns are supported", - "scope": "resource" - }, - "robotcode.robocop.configurations": { - "type": "array", - "default": [], - "items": { - "type": "string" - }, - "description": "Configure 'robocop' checker with parameter value.", - "scope": "resource" - } - } - } - ], - "commands": [ - { - "command": "robotcode.runSuite", - "category": "RobotCode", - "title": "Run Suite", - "enablement": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder", - "icon": { - "light": "resources/light/run_suite.svg", - "dark": "resources/dark/run_suite.svg" - } + "configuration": [ + { + "title": "RobotCode", + "type": "object", + "properties": { + "robotcode.python": { + "type": "string", + "default": "", + "description": "Specifies the python executable to be used for RobotCode. If no path is specified, try to get it from \"python\" extension. Requires a VSCode restart to take effect.", + "scope": "resource" + }, + "robotcode.editor.4SpacesTab": { + "type": "boolean", + "default": true, + "description": "If actived insert 4 spaces if TAB is pressed." + }, + "robotcode.languageServer.mode": { + "type": "string", + "default": "stdio", + "description": "Specifies the mode the language server is started. Requires a VSCode restart to take effect.", + "enum": [ + "stdio", + "tcp" + ], + "scope": "resource" + }, + "robotcode.languageServer.tcpPort": { + "type": "number", + "default": 0, + "description": "If the port is specified, connect to the language server previously started at the given port. Requires a VSCode restart to take effect.", + "scope": "resource" + }, + "robotcode.languageServer.args": { + "type": "array", + "default": [], + "items": { + "type": "string" }, - { - "command": "robotcode.debugSuite", - "category": "RobotCode", - "title": "Debug Suite", - "enablement": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder", - "icon": { - "light": "resources/light/debug_suite.svg", - "dark": "resources/dark/debug_suite.svg" - } + "description": "Specifies the arguments to be passed to the language server (i.e.: [\"--log\", \"--log-file=~/robotcode.log\"]). Requires a VSCode restart to take effect.", + "scope": "resource" + }, + "robotcode.debugAdapter.mode": { + "type": "string", + "default": "stdio", + "description": "Specifies the mode the debug adapter is started. Requires a VSCode restart to take effect.", + "enum": [ + "stdio", + "tcp" + ], + "scope": "resource" + }, + "robotcode.debugAdapter.tcpPort": { + "type": "number", + "default": 0, + "description": "If the port is specified, connect to the debug adapter previously started at the given port. Requires a VSCode restart to take effect.", + "scope": "resource" + }, + "robotcode.debugAdapter.host": { + "type": "string", + "default": null, + "description": "If the host is specified, connect to the debug adapter previously started at the given host. Requires a VSCode restart to take effect.", + "scope": "resource" + }, + "robotcode.debugAdapter.args": { + "type": "array", + "default": [], + "items": { + "type": "string" }, - { - "command": "robotcode.runTest", - "category": "RobotCode", - "title": "Run Test Case", - "enablement": "resourceLangId == robotframework && resourceExtname == .robot", - "icon": { - "light": "resources/light/run_test.svg", - "dark": "resources/dark/run_test.svg" - } + "description": "Specifies the arguments to be passed to the debug adapter (i.e.: [\"--log\", \"--log-file=~/debug-adapter.log\"]). Requires a VSCode restart to take effect.", + "scope": "resource" + }, + "robotcode.robot.args": { + "type": "array", + "default": [ + "--outputdir", + "results" + ], + "items": { + "type": "string" }, - { - "command": "robotcode.debugTest", - "category": "RobotCode", - "title": "Debug Test Case", - "enablement": "resourceLangId == robotframework && resourceExtname == .robot", - "icon": { - "light": "resources/light/debug_test.svg", - "dark": "resources/dark/debug_test.svg" - } - } - ], - "menus": { - "editor/title/run": [ - { - "command": "robotcode.runTest", - "group": "robotcode@1run@1", - "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" - }, - { - "command": "robotcode.debugTest", - "group": "robotcode@2debug@1", - "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" - }, - { - "command": "robotcode.runSuite", - "group": "robotcode@1run@2", - "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" - }, - { - "command": "robotcode.debugSuite", - "group": "robotcode@2debug@2", - "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" - } + "description": "Specifies the arguments to be passed to robot module.", + "scope": "resource" + }, + "robotcode.robot.pythonPath": { + "type": "array", + "default": [ + "./lib", + "./resources" + ], + "items": { + "type": "string" + }, + "description": "Specifies additional python paths to robotframework. Corresponds to the '--pythonpath' option of the robot module.", + "scope": "resource" + }, + "robotcode.robot.env": { + "type": "object", + "default": {}, + "description": "Specifies the environment variables for robotframework.", + "scope": "resource" + }, + "robotcode.robot.variables": { + "type": "object", + "default": {}, + "description": "Specifies the variables for robotframework. Corresponds to the '--variable name:value' option of the robot module.", + "scope": "resource" + }, + "robotcode.debug.defaultConfiguration": { + "type": "object", + "default": {}, + "description": "Specifies the default configuration to run or debug robot tests.", + "scope": "resource" + }, + "robotcode.debug.defaultConsole": { + "type": "string", + "enum": [ + "internalConsole", + "integratedTerminal", + "externalTerminal" + ], + "default": "integratedTerminal", + "enumDescriptions": [ + "No terminal (pipes the output to the client debug console).", + "Use terminal integrated in client.", + "External terminal (configured in user settings)." + ], + "description": "Default setting for where to launch the debug target: internal console, integrated terminal, or external terminal.", + "scope": "resource" + }, + "robotcode.robocop.enabled": { + "type": "boolean", + "default": true, + "description": "Enables 'robocop' code analysis, if installed.", + "scope": "resource" + }, + "robotcode.syntax.sectionStyle": { + "type": "string", + "default": "*** {name}s ***", + "description": "Defines the section style format.", + "scope": "resource" + }, + "robotcode.run.openReportAfterRun": { + "type": "string", + "enum": [ + "disabled", + "external" ], - "editor/context": [ - { - "command": "robotcode.runTest", - "group": "robotcode@1run@1", - "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" - }, - { - "command": "robotcode.debugTest", - "group": "robotcode@2debug@1", - "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" - }, - { - "command": "robotcode.runSuite", - "group": "robotcode@1run@2", - "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" - }, - { - "command": "robotcode.debugSuite", - "group": "robotcode@2debug@2", - "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" - } + "enumDescriptions": [ + "Do not open the result report", + "Opens the report in an external browser." ], - "explorer/context": [ - { - "command": "robotcode.runSuite", - "group": "robotcode@1", - "when": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder" - }, - { - "command": "robotcode.debugSuite", - "group": "robotcode@2", - "when": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder" - } - ] + "default": "none", + "description": "Defines if the test report should be opened a run session automatically.", + "scope": "resource" + }, + "robotcode.robocop.include": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "Ignore specified 'robocop' rules. You can define rule by its name or id. Glob patterns are supported", + "scope": "resource" + }, + "robotcode.robocop.exclude": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "Ignore specified 'robocop' rules. You can define rule by its name or id. Glob patterns are supported", + "scope": "resource" + }, + "robotcode.robocop.configurations": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "Configure 'robocop' checker with parameter value.", + "scope": "resource" + } + } + } + ], + "commands": [ + { + "command": "robotcode.runSuite", + "category": "RobotCode", + "title": "Run Suite", + "enablement": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder", + "icon": { + "light": "resources/light/run_suite.svg", + "dark": "resources/dark/run_suite.svg" + } + }, + { + "command": "robotcode.debugSuite", + "category": "RobotCode", + "title": "Debug Suite", + "enablement": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder", + "icon": { + "light": "resources/light/debug_suite.svg", + "dark": "resources/dark/debug_suite.svg" + } + }, + { + "command": "robotcode.runTest", + "category": "RobotCode", + "title": "Run Test Case", + "enablement": "resourceLangId == robotframework && resourceExtname == .robot", + "icon": { + "light": "resources/light/run_test.svg", + "dark": "resources/dark/run_test.svg" + } + }, + { + "command": "robotcode.debugTest", + "category": "RobotCode", + "title": "Debug Test Case", + "enablement": "resourceLangId == robotframework && resourceExtname == .robot", + "icon": { + "light": "resources/light/debug_test.svg", + "dark": "resources/dark/debug_test.svg" + } + } + ], + "menus": { + "editor/title/run": [ + { + "command": "robotcode.runTest", + "group": "robotcode@1run@1", + "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" }, - "breakpoints": [ - { - "language": "robotframework" - } + { + "command": "robotcode.debugTest", + "group": "robotcode@2debug@1", + "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" + }, + { + "command": "robotcode.runSuite", + "group": "robotcode@1run@2", + "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" + }, + { + "command": "robotcode.debugSuite", + "group": "robotcode@2debug@2", + "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" + } + ], + "editor/context": [ + { + "command": "robotcode.runTest", + "group": "robotcode@1run@1", + "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" + }, + { + "command": "robotcode.debugTest", + "group": "robotcode@2debug@1", + "when": "resourceLangId == robotframework && resourceExtname == .robot && robotCode.editor.inTest && !isInDiffEditor" + }, + { + "command": "robotcode.runSuite", + "group": "robotcode@1run@2", + "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" + }, + { + "command": "robotcode.debugSuite", + "group": "robotcode@2debug@2", + "when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor" + } + ], + "explorer/context": [ + { + "command": "robotcode.runSuite", + "group": "robotcode@1", + "when": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder" + }, + { + "command": "robotcode.debugSuite", + "group": "robotcode@2", + "when": "resourceLangId == robotframework && resourceExtname == .robot || explorerResourceIsFolder" + } + ] + }, + "breakpoints": [ + { + "language": "robotframework" + } + ], + "debuggers": [ + { + "type": "robotcode", + "label": "RobotCode Debug", + "languages": [ + "robotframework" ], - "debuggers": [ - { - "type": "robotcode", - "label": "RobotCode Debug", - "languages": [ - "robotframework" + "configurationAttributes": { + "launch": { + "properties": { + "target": { + "type": "string", + "description": "The .robot file or a folder containing .robot files to be launched.", + "default": "${file}" + }, + "args": { + "type": "array", + "description": "Specifies additional command line arguments passed to robot.", + "default": [] + }, + "cwd": { + "type": "string", + "description": "Absolute path to the working directory of the program being debugged. Default is the root directory of the file (leave empty).", + "default": "${workspaceFolder}" + }, + "env": { + "type": "object", + "description": "Environment variables defined as a key value pair.", + "default": [] + }, + "console": { + "type": "string", + "enum": [ + "internalConsole", + "integratedTerminal", + "externalTerminal" ], - "configurationAttributes": { - "launch": { - "properties": { - "target": { - "type": "string", - "description": "The .robot file or a folder containing .robot files to be launched.", - "default": "${file}" - }, - "args": { - "type": "array", - "description": "Specifies additional command line arguments passed to robot.", - "default": [] - }, - "cwd": { - "type": "string", - "description": "Absolute path to the working directory of the program being debugged. Default is the root directory of the file (leave empty).", - "default": "${workspaceFolder}" - }, - "env": { - "type": "object", - "description": "Environment variables defined as a key value pair.", - "default": [] - }, - "console": { - "type": "string", - "enum": [ - "internalConsole", - "integratedTerminal", - "externalTerminal" - ], - "enumDescriptions": [ - "No terminal (pipes the output to the client debug console).", - "Use terminal integrated in client.", - "External terminal (configured in user settings)." - ], - "description": "Where to launch the debug target: internal console, integrated terminal, or external terminal.", - "default": "integratedTerminal" - }, - "python": { - "type": "string", - "description": "Specifies the python interpreter to use. If not specified, the python interpreter defined for the extension is used.", - "default": null - }, - "robotPythonPath": { - "type": "array", - "description": "Specifies additional python paths for robotframework. Corresponds to the '--pythonpath' option of the robot module.", - "default": [] - }, - "attachPython": { - "type": "boolean", - "description": "Attach also the python debugger if a robot test starts.", - "default": false - }, - "pythonConfiguration": { - "type": "object", - "description": "Defines a template for the python launch configuration.", - "default": {} - }, - "variables": { - "type": "object", - "default": {}, - "description": "Specifies the variables for robotframework. Corresponds to the '--variable name:value' option of the robot module." - }, - "launcherArgs": { - "type": "array", - "description": "Command line arguments passed to launcher.", - "default": [] - }, - "launcherTimeout": { - "type": "number", - "description": "Timeout to connect to the debuggee.", - "default": 5 - }, - "outputMessages": { - "type": "boolean", - "description": "Output messages from robotframework in debug console.", - "default": false - }, - "outputLog": { - "type": "boolean", - "description": "Output log messages from robotframework in debug console.", - "default": false - }, - "groupOutput": { - "type": "boolean", - "description": "Group start and stop suite/test/keyword messages in debug console.", - "default": false - } - } - } - }, - "initialConfigurations": [], - "configurationSnippets": [ - { - "label": "RobotCode: Launch .robot file", - "description": "Add a new configuration for launching Robot Framework Tests.", - "body": { - "type": "robotcode", - "name": "RobotCode: Launch .robot file", - "request": "launch", - "cwd": "^\"\\${workspaceFolder}\"", - "target": "^\"\\${file}\"", - "env": {}, - "args": [] - } - } - ] + "enumDescriptions": [ + "No terminal (pipes the output to the client debug console).", + "Use terminal integrated in client.", + "External terminal (configured in user settings)." + ], + "description": "Where to launch the debug target: internal console, integrated terminal, or external terminal.", + "default": "integratedTerminal" + }, + "python": { + "type": "string", + "description": "Specifies the python interpreter to use. If not specified, the python interpreter defined for the extension is used.", + "default": null + }, + "robotPythonPath": { + "type": "array", + "description": "Specifies additional python paths for robotframework. Corresponds to the '--pythonpath' option of the robot module.", + "default": [] + }, + "attachPython": { + "type": "boolean", + "description": "Attach also the python debugger if a robot test starts.", + "default": false + }, + "pythonConfiguration": { + "type": "object", + "description": "Defines a template for the python launch configuration.", + "default": {} + }, + "variables": { + "type": "object", + "default": {}, + "description": "Specifies the variables for robotframework. Corresponds to the '--variable name:value' option of the robot module." + }, + "launcherArgs": { + "type": "array", + "description": "Command line arguments passed to launcher.", + "default": [] + }, + "launcherTimeout": { + "type": "number", + "description": "Timeout to connect to the debuggee.", + "default": 5 + }, + "outputMessages": { + "type": "boolean", + "description": "Output messages from robotframework in debug console.", + "default": false + }, + "outputLog": { + "type": "boolean", + "description": "Output log messages from robotframework in debug console.", + "default": false + }, + "groupOutput": { + "type": "boolean", + "description": "Group start and stop suite/test/keyword messages in debug console.", + "default": false + } } + } + }, + "initialConfigurations": [], + "configurationSnippets": [ + { + "label": "RobotCode: Launch .robot file", + "description": "Add a new configuration for launching Robot Framework Tests.", + "body": { + "type": "robotcode", + "name": "RobotCode: Launch .robot file", + "request": "launch", + "cwd": "^\"\\${workspaceFolder}\"", + "target": "^\"\\${file}\"", + "env": {}, + "args": [] + } + } ] - }, - "scripts": { - "vscode:prepublish": "webpack --mode production", - "webpack": "webpack --mode development", - "webpack-dev": "webpack --mode development --watch", - "test-compile": "tsc -p ./", - "compile": "tsc -p ./", - "watch": "tsc -watch -p ./", - "pretest": "npm run compile && npm run lint", - "lint": "eslint vscode-client --ext ts", - "test": "node ./out/test/runTest.js" - }, - "extensionDependencies": [ - "ms-python.python" - ], - "dependencies": { - "open": "^8.2.1", - "vscode-debugadapter": "^1.47.0", - "vscode-debugprotocol": "^1.47.0", - "vscode-languageclient": "^7.0.0", - "vscode-languageserver-protocol": "^3.16.0", - "vscode-test-adapter-api": "^1.9.0", - "vscode-test-adapter-util": "^0.7.1" - }, - "devDependencies": { - "@types/glob": "^7.1.4", - "@types/mocha": "^8.2.3", - "@types/node": "^16.0.0", - "@types/vscode": "^1.57.1", - "@typescript-eslint/eslint-plugin": "^4.28.2", - "@typescript-eslint/parser": "^4.28.2", - "eslint": "^7.30.0", - "glob": "^7.1.7", - "mocha": "^9.0.2", - "ts-loader": "^9.2.3", - "typescript": "^4.3.5", - "vsce": "^1.95.0", - "vscode-debugadapter-testsupport": "^1.47.0", - "vscode-test": "^1.5.2", - "webpack": "^5.43.0", - "webpack-cli": "^4.7.2" - } + } + ] + }, + "scripts": { + "vscode:prepublish": "webpack --mode production", + "webpack": "webpack --mode development", + "webpack-dev": "webpack --mode development --watch", + "test-compile": "tsc -p ./", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile && npm run lint", + "lint": "eslint --ext .ts,.tsx,.js .", + "lint-fix": "eslint --ext .ts,.tsx,.js --fix .", + "test": "node ./out/test/runTest.js", + "download-api": "vscode-dts dev", + "postdownload-api": "vscode-dts main", + "postinstall": "npm run download-api" + }, + "extensionDependencies": [ + "ms-python.python" + ], + "dependencies": { + "open": "^8.2.1", + "vscode-debugadapter": "^1.47.0", + "vscode-debugprotocol": "^1.47.0", + "vscode-languageclient": "^7.0.0", + "vscode-languageserver-protocol": "^3.16.0", + "vscode-test-adapter-api": "^1.9.0", + "vscode-test-adapter-util": "^0.7.1" + }, + "devDependencies": { + "@types/glob": "^7.1.4", + "@types/mocha": "^8.2.3", + "@types/node": "^16.3.1", + "@types/vscode": "^1.58.0", + "@typescript-eslint/eslint-plugin": "^4.28.2", + "@typescript-eslint/parser": "^4.28.2", + "eslint": "^7.30.0", + "eslint-config-airbnb": "^18.2.1", + "eslint-config-prettier": "^8.3.0", + "eslint-config-standard": "^16.0.3", + "eslint-config-standard-with-typescript": "20.0.0", + "eslint-plugin-import": "^2.23.4", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^3.4.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-react": "^7.24.0", + "eslint-plugin-react-hooks": "^4.2.0", + "glob": "^7.1.7", + "mocha": "^9.0.2", + "prettier": "^2.3.2", + "ts-loader": "^9.2.3", + "typescript": "^4.3.5", + "vsce": "^1.95.1", + "vscode-debugadapter-testsupport": "^1.47.0", + "vscode-dts": "^0.3.1", + "vscode-test": "^1.5.2", + "webpack": "^5.44.0", + "webpack-cli": "^4.7.2" + } } diff --git a/syntaxes/robotframework.tmLanguage.json b/syntaxes/robotframework.tmLanguage.json index f92885703..38aa025ab 100644 --- a/syntaxes/robotframework.tmLanguage.json +++ b/syntaxes/robotframework.tmLanguage.json @@ -1,1033 +1,1031 @@ { - "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", - "fileTypes": [ - "robotframework" - ], - "repository": { - "settings_table": { - "begin": "(?i)^(\\*+ ?(?:Settings|Setting)[ *]*)(?= {2,}| ?\\t| ?$)", - "beginCaptures": { - "1": { - "name": "storage.type.section.settings.robot" - } - }, - "end": "^(?=\\*)", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#variables" - }, - { - "include": "#documentation" - }, - { - "include": "#settings_table_tags" - }, - { - "include": "#settings_table_setting" - }, - { - "include": "#settings_table_setting_keywordCalls" - }, - { - "include": "#settings_table_setting_imports" - } - ], - "name": "meta.table.settings.robot" - }, - "settings_table_setting": { - "begin": "(?i)^(Test Timeout|Task Timeout)(?= {2}| ?\\t| ?$)", - "beginCaptures": { - "1": { - "name": "storage.type.setting.robot" - } - }, - "contentName": "string.quoted.single.robot", - "end": "^(?!\\s*\\.\\.\\.)", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#variables" - }, - { - "include": "#line_continuation" - } - ], - "name": "meta.setting.documentation.robot" - }, - "settings_table_tags": { - "begin": "(?i)^(Force Tags|Default Tags)(?= {2}| ?\\t| ?$)", - "beginCaptures": { - "1": { - "name": "keyword.robot" - } - }, - "contentName": "entity.name.tag.robot", - "end": "^(?!\\s*\\.\\.\\.)", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#variables" - }, - { - "include": "#line_continuation" - } - ], - "name": "meta.setting.tags.robot" - }, - "with_name": {}, - "settings_table_setting_imports": { - "begin": "(?i)^(Library|Resource|Variables)(?: {2,}| ?\\t)+((.*?)(?= {2}| ?\\t| ?$))?", - "beginCaptures": { - "1": { - "name": "keyword.control.import.robot" - }, - "2": { - "name": "entity.name.namespace.robot" - } - }, - "end": "^(?!\\s*\\.\\.\\.)", - "contentName": "string.quoted.double", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#variables" - }, - { - "include": "#line_continuation" - }, - { - "include": "#named_argument" - }, - { - "begin": "(?: {2,}| ?\\t)(WITH NAME)(?= {2}| ?\\t| ?$)", - "end": "^(?!\\s*\\.\\.\\.)", - "beginCaptures": { - "1": { - "name": "keyword.other.robot" - } - }, - "contentName": "variable.name", - "name": "meta.setting.library.name.robot", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#line_continuation" - } - ] - } - ], - "name": "meta.setting.imports.robot" - }, - "settings_table_setting_keywordCalls": { - "begin": "(?i)^(Suite Setup|Suite Teardown|Test Setup|Task Setup|Test Teardown|Task Teardown|Test Template|Task Template|Metadata)((?: {2,}| ?\\t)+(.*?)(?= {2}| ?\\t| ?$))?", - "beginCaptures": { - "1": { - "name": "keyword.control.robot" - }, - "2": { - "name": "meta.support.function.robot" - } - }, - "end": "^(?!\\s*\\.\\.\\.)", - "contentName": "string.quoted.single.robot", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#variables" - }, - { - "include": "#line_continuation" - }, - { - "include": "#named_argument" - } - ], - "name": "meta.setting.kwcalls.robot" - }, - "documentation": { - "end": "^(?!\\s*\\.\\.\\.)", - "begin": "(?i)^(Documentation)(?= {2}| ?\\t| ?$)", - "beginCaptures": { - "1": { - "name": "keyword.control.robot" - } - }, - "contentName": "comment.block.documentation", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#line_continuation" - } - ], - "name": "meta.documentation.robot" - }, - "variables_table": { - "begin": "(?i)^(\\*+ ?(?:Variables|Variable)[ *]*)(?= {2,}| ?\\t| ?$)", - "beginCaptures": { - "1": { - "name": "storage.type.section.variables.robot" - } - }, - "end": "^(?=\\*)", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#variables" - }, - { - "include": "#named_argument" - }, - { - "include": "#variable_assign" - }, - { - "include": "#line_continuation" - } - ], - "name": "meta.table.variables.robot" - }, - "variable_assign": { - "begin": "^(?:([$@&%]\\{)(.+?}*)(\\})( ?=)?)", - "beginCaptures": { - "1": { - "name": "keyword.operator.robot" - }, - "2": { - "name": "variable.parameter.robot" - }, - "3": { - "name": "keyword.operator.robot" - }, - "4": { - "name": "keyword.operator.robot" - } - }, - "contentName": "string.quoted.single.robot", - "end": "^(?!\\s*\\.\\.\\.)", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#line_continuation" - }, - { - "include": "#variables" - }, - { - "include": "#named_argument" - } - ], - "name": "meta.variable.assign.robot" - }, - "test_cases_table": { - "begin": "(?i)^(\\*+ ?(?:Test Cases|Test Case|Tasks|Task)[ *]*)(?= {2,}| ?\\t| ?$)", - "beginCaptures": { - "1": { - "name": "storage.type.section.testcases.robot" - } - }, - "end": "^(?=\\*)", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#function_declaration" - }, - { - "include": "#function_documentation" - }, - { - "include": "#returning_keyword_call" - }, - { - "include": "#control_keywords" - }, - { - "include": "#line_continuation" - }, - { - "include": "#variables" - }, - { - "include": "#function_tags" - }, - { - "include": "#function_timeout" - }, - { - "include": "#function_fixture" - }, - { - "include": "#function_template" - }, - { - "include": "#keyword_call" - }, - { - "include": "#named_argument" - } - ], - "name": "meta.table.testcases.robot" - }, - "keywords_table": { - "begin": "(?i)^(\\*+ ?(?:Keywords|Keyword)[ *]*)(?= {2,}| ?\\t| ?$)", - "beginCaptures": { - "1": { - "name": "storage.type.section.keywords.robot" - } - }, - "end": "^(?=\\*)", - "patterns": [ - { - "include": "#escape" - }, - { - "include": "#comment" - }, - { - "include": "#function_declaration" - }, - { - "include": "#function_documentation" - }, - { - "include": "#returning_keyword_call" - }, - { - "include": "#control_keywords" - }, - { - "include": "#line_continuation" - }, - { - "include": "#variables" - }, - { - "include": "#function_tags" - }, - { - "include": "#keyword_arguments" - }, - { - "include": "#function_return" - }, - { - "include": "#function_timeout" - }, - { - "include": "#function_teardown" - }, - { - "include": "#keyword_call" - }, - { - "include": "#named_argument" - } - ], - "name": "meta.table.keywords.robot" - }, - "comments_table": { - "end": "^(?=\\*)", - "begin": "(?i)^(\\*+\\s*comments?[\\s*].*)|([^\\*]*)", - "beginCaptures": { - "1": { - "name": "storage.type.section.comments.robot" - }, - "2": { - "name": "comment.line.robot" - } - }, - "contentName": "comment.line.robot", - "name": "meta.table.comments.robot" - }, - "variables": { - "match": "(?i)(?:(?:(? { - return debugConfiguration; + constructor(private readonly pythonManager: PythonManager) {} + + resolveDebugConfigurationWithSubstitutedVariables?( + folder: vscode.WorkspaceFolder | undefined, + debugConfiguration: vscode.DebugConfiguration, + _token?: vscode.CancellationToken + ): vscode.ProviderResult { + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); + + try { + if (path.isAbsolute(debugConfiguration.target)) { + debugConfiguration.target = path.relative(debugConfiguration.cwd, debugConfiguration.target).toString(); + } + } catch {} + + if (!debugConfiguration.python) debugConfiguration.python = this.pythonManager.getPythonCommand(folder); + + debugConfiguration.robotPythonPath = [ + ...config.get>("robot.pythonPath", []), + ...(debugConfiguration.robotPythonPath ?? []), + ]; + + debugConfiguration.args = [...config.get>("robot.args", []), ...(debugConfiguration.args ?? [])]; + + debugConfiguration.variables = { + ...config.get<{ [Key: string]: unknown }>("robot.variables", {}), + ...(debugConfiguration.variables ?? {}), + }; + + debugConfiguration.env = { + ...config.get<{ [Key: string]: unknown }>("robot.env", {}), + ...(debugConfiguration.env ?? {}), + }; + + // if (pythonDebugpyPath) { + // debugConfiguration.env = { PYTHONPATH: path.dirname(pythonDebugpyPath), ...debugConfiguration.env }; + // } + + if (!debugConfiguration.attachPython || debugConfiguration.noDebug) { + debugConfiguration.attachPython = false; } - resolveDebugConfigurationWithSubstitutedVariables?( - folder: vscode.WorkspaceFolder | undefined, - debugConfiguration: vscode.DebugConfiguration, - token?: vscode.CancellationToken - ): vscode.ProviderResult { - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); - - try { - if (path.isAbsolute(debugConfiguration.target)) - debugConfiguration.target = path.relative(debugConfiguration.cwd, debugConfiguration.target).toString(); - } catch {} - - if (!debugConfiguration.python) debugConfiguration.python = this.pythonManager.getPythonCommand(folder); - - debugConfiguration.robotPythonPath = [ - ...config.get>("robot.pythonPath", []), - ...(debugConfiguration.robotPythonPath ?? []), - ]; - - debugConfiguration.args = [...config.get>("robot.args", []), ...(debugConfiguration.args ?? [])]; - - debugConfiguration.variables = { - ...config.get("robot.variables", {}), - ...(debugConfiguration.variables ?? {}), - }; - - debugConfiguration.env = { ...config.get("robot.env", {}), ...(debugConfiguration.env ?? {}) }; - - // if (pythonDebugpyPath) { - // debugConfiguration.env = { PYTHONPATH: path.dirname(pythonDebugpyPath), ...debugConfiguration.env }; - // } + const template = config.get("debug.defaultConfiguration", {}); - if (!debugConfiguration.attachPython || debugConfiguration.noDebug) { - debugConfiguration.attachPython = false; - } - - let template = config.get("debug.defaultConfiguration", {}); - - return { ...template, ...debugConfiguration }; - } + return { ...template, ...debugConfiguration }; + } } class RobotCodeDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { - constructor(private readonly pythonManager: PythonManager) {} - createDebugAdapterDescriptor( - session: vscode.DebugSession, - executable: vscode.DebugAdapterExecutable | undefined - ): vscode.ProviderResult { - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, session.workspaceFolder); + constructor(private readonly pythonManager: PythonManager) {} - let mode = config.get("debugAdapter.mode", "stdio"); + createDebugAdapterDescriptor( + session: vscode.DebugSession, + _executable: vscode.DebugAdapterExecutable | undefined + ): vscode.ProviderResult { + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, session.workspaceFolder); - switch (mode) { - case "stdio": - let pythonCommand = this.pythonManager.getPythonCommand(session.workspaceFolder); + const mode = config.get("debugAdapter.mode", "stdio"); - if (pythonCommand === undefined) { - throw new Error("Can't get a valid python command."); - } + switch (mode) { + case "stdio": { + const pythonCommand = this.pythonManager.getPythonCommand(session.workspaceFolder); - let debugAdapterArgs = config.get>("debugAdapter.args", []); + if (pythonCommand === undefined) { + throw new Error("Can't get a valid python command."); + } - let args: Array = ["-u", this.pythonManager.pythonDebugAdapterMain!, "--mode", "stdio"].concat( - debugAdapterArgs - ); + const debugAdapterArgs = config.get>("debugAdapter.args", []); - const options: vscode.DebugAdapterExecutableOptions = { - env: {}, - cwd: session.workspaceFolder?.uri.fsPath, - }; + const args: Array = ["-u", this.pythonManager.pythonDebugAdapterMain!, "--mode", "stdio"].concat( + debugAdapterArgs + ); - return new vscode.DebugAdapterExecutable(pythonCommand, args, options); + const options: vscode.DebugAdapterExecutableOptions = { + env: {}, + cwd: session.workspaceFolder?.uri.fsPath, + }; - case "tcp": - let port = - config.get("debugAdapter.tcpPort", DEBUG_ADAPTER_DEFAULT_TCP_PORT) || - DEBUG_ADAPTER_DEFAULT_TCP_PORT; + return new vscode.DebugAdapterExecutable(pythonCommand, args, options); + } + case "tcp": { + const port = + config.get("debugAdapter.tcpPort", DEBUG_ADAPTER_DEFAULT_TCP_PORT) || DEBUG_ADAPTER_DEFAULT_TCP_PORT; - let host = config.get("debugAdapter.host", DEBUG_ADAPTER_DEFAULT_HOST) || DEBUG_ADAPTER_DEFAULT_HOST; + const host = config.get("debugAdapter.host", DEBUG_ADAPTER_DEFAULT_HOST) || DEBUG_ADAPTER_DEFAULT_HOST; - return new vscode.DebugAdapterServer(port, host); - default: - throw new Error("Unsupported Mode."); - } + return new vscode.DebugAdapterServer(port, host); + } + default: + throw new Error("Unsupported Mode."); } + } } export class DebugManager { - private _disposables: vscode.Disposable; - - constructor( - public readonly extensionContext: vscode.ExtensionContext, - public readonly pythonManager: PythonManager, - public readonly languageClientsManager: LanguageClientsManager, - public readonly outputChannel: vscode.OutputChannel - ) { - this._disposables = vscode.Disposable.from( - vscode.commands.registerCommand("robotcode.runSuite", async (resource) => { - return await this.debugSuiteOrTestcase( - resource ?? vscode.window.activeTextEditor?.document.uri, - undefined, - { - noDebug: true, - } - ); - }), - - vscode.commands.registerCommand("robotcode.debugSuite", async (resource) => { - return await this.debugSuiteOrTestcase( - resource ?? vscode.window.activeTextEditor?.document.uri, - undefined - ); - }), - - vscode.commands.registerCommand( - "robotcode.runTest", - async (resource: vscode.Uri | string | undefined, test) => { - let res = resource ?? vscode.window.activeTextEditor?.document.uri; - - let realTest = - (test !== undefined && typeof test === "string" ? test.toString() : undefined) ?? - (await this.languageClientsManager.getTestFromResource(res)); - if (!realTest) return; - - return await this.debugSuiteOrTestcase(res, realTest, { - noDebug: true, - }); - } - ), - - vscode.commands.registerCommand( - "robotcode.debugTest", - async (resource: vscode.Uri | string | undefined, test) => { - let res = resource ?? vscode.window.activeTextEditor?.document.uri; - - let realTest = - (test !== undefined && typeof test === "string" ? test.toString() : undefined) ?? - (await this.languageClientsManager.getTestFromResource(res)); - if (!realTest) return; - - return await this.debugSuiteOrTestcase(res, realTest); - } - ), - vscode.debug.registerDebugConfigurationProvider( - "robotcode", - new RobotCodeDebugConfigurationProvider(this.pythonManager) - ), - vscode.debug.registerDebugAdapterDescriptorFactory( - "robotcode", - new RobotCodeDebugAdapterDescriptorFactory(this.pythonManager) - ), - // vscode.debug.registerDebugAdapterTrackerFactory("robotcode", { - // createDebugAdapterTracker(session: vscode.DebugSession) { - // return { - // onWillStartSession: () => OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER start session`), - // onWillStopSession: () => OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER stop session`), - // onWillReceiveMessage: (m) => - // OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER > ${JSON.stringify(m, undefined, 2)}`), - // onDidSendMessage: (m) => - // OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER < ${JSON.stringify(m, undefined, 2)}`), - // onError: (e) => - // OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER ERROR: ${JSON.stringify(e, undefined, 2)}`), - // onExit: (c, s) => OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER EXIT code ${c} signal ${s}`), - // }; - // }, - // }), - vscode.debug.onDidReceiveDebugSessionCustomEvent(async (event) => { - if (event.session.configuration.type === "robotcode") { - switch (event.event) { - case "debugpyStarted": { - await this.attachPython(event.session, event.event, event.body); - break; - } - case "robotExited": { - await this.onRobotExited( - event.session, - event.body.outputFile, - event.body.logFile, - event.body.reportFile - ); - break; - } - } - } - }), - vscode.languages.registerInlineValuesProvider("robotframework", { - provideInlineValues( - document: vscode.TextDocument, - viewPort: vscode.Range, - context: vscode.InlineValueContext, - token: vscode.CancellationToken - ): vscode.ProviderResult { - const allValues: vscode.InlineValue[] = []; - - for ( - let l = viewPort.start.line; - l <= Math.min(viewPort.end.line, context.stoppedLocation.end.line); - l++ - ) { - const line = document.lineAt(l); - let text = line.text.split("#")[0]; - - const variableMatches = - /([$@&%]\{)(?:((?:\d+\.?\d*)|(?:0x[\/da-f]+)|(?:0o[0-7]+)|(?:0b[01]+))|(true|false|none|null|empty|space|\/|:|\\n)|((.+?}*)))(\})(?:(\[)(?:(\d+)|(.*?))?(\]))?/gi; - - let match; - while ((match = variableMatches.exec(text))) { - let varName = match[0]; - - const rng = new vscode.Range(l, match.index, l, match.index + varName.length); - allValues.push(new vscode.InlineValueVariableLookup(rng, varName, false)); - } - } - return allValues; - }, - }) - ); - } - - async dispose() { - this._disposables.dispose(); - } - - async debugSuiteOrTestcase( - resource: string | vscode.Uri | undefined, - testcases?: string | string[], - options?: vscode.DebugSessionOptions - ) { - if (resource === undefined) return; - - let uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); - - let folder = vscode.workspace.getWorkspaceFolder(uri); - - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); - - let args = []; - - if (testcases) { - if (!(testcases instanceof Array)) { - testcases = [testcases]; + private _disposables: vscode.Disposable; + + constructor( + public readonly extensionContext: vscode.ExtensionContext, + public readonly pythonManager: PythonManager, + public readonly languageClientsManager: LanguageClientsManager, + public readonly outputChannel: vscode.OutputChannel + ) { + this._disposables = vscode.Disposable.from( + vscode.commands.registerCommand( + "robotcode.runSuite", + async (resource) => + await DebugManager.debugSuiteOrTestcase(resource ?? vscode.window.activeTextEditor?.document.uri, undefined, { + noDebug: true, + }) + ), + + vscode.commands.registerCommand( + "robotcode.debugSuite", + async (resource) => + await DebugManager.debugSuiteOrTestcase(resource ?? vscode.window.activeTextEditor?.document.uri, undefined) + ), + + vscode.commands.registerCommand("robotcode.runTest", async (resource: vscode.Uri | string | undefined, test) => { + const res = resource ?? vscode.window.activeTextEditor?.document.uri; + + const realTest = + (test !== undefined && typeof test === "string" ? test.toString() : undefined) ?? + (await this.languageClientsManager.getTestFromResource(res)); + if (!realTest) return; + + return await DebugManager.debugSuiteOrTestcase(res, realTest, { + noDebug: true, + }); + }), + + vscode.commands.registerCommand( + "robotcode.debugTest", + async (resource: vscode.Uri | string | undefined, test) => { + const res = resource ?? vscode.window.activeTextEditor?.document.uri; + + const realTest = + (test !== undefined && typeof test === "string" ? test.toString() : undefined) ?? + (await this.languageClientsManager.getTestFromResource(res)); + if (!realTest) return; + + return await DebugManager.debugSuiteOrTestcase(res, realTest); + } + ), + vscode.debug.registerDebugConfigurationProvider( + "robotcode", + new RobotCodeDebugConfigurationProvider(this.pythonManager) + ), + vscode.debug.registerDebugAdapterDescriptorFactory( + "robotcode", + new RobotCodeDebugAdapterDescriptorFactory(this.pythonManager) + ), + // vscode.debug.registerDebugAdapterTrackerFactory("robotcode", { + // createDebugAdapterTracker(session: vscode.DebugSession) { + // return { + // onWillStartSession: () => OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER start session`), + // onWillStopSession: () => OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER stop session`), + // onWillReceiveMessage: (m) => + // OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER > ${JSON.stringify(m, undefined, 2)}`), + // onDidSendMessage: (m) => + // OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER < ${JSON.stringify(m, undefined, 2)}`), + // onError: (e) => + // OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER ERROR: ${JSON.stringify(e, undefined, 2)}`), + // onExit: (c, s) => OUTPUT_CHANNEL.appendLine(`DEBUG_ADAPTER EXIT code ${c} signal ${s}`), + // }; + // }, + // }), + vscode.debug.onDidReceiveDebugSessionCustomEvent(async (event) => { + if (event.session.configuration.type === "robotcode") { + switch (event.event) { + case "debugpyStarted": { + await DebugManager.attachPython(event.session, event.event, event.body); + break; } - for (let testcase of testcases) { - args.push("-t"); - args.push(testcase.toString()); + case "robotExited": { + await DebugManager.onRobotExited( + event.session, + event.body.outputFile, + event.body.logFile, + event.body.reportFile + ); + break; } + } } - - let template = config.get("debug.defaultConfiguration", {}); - - vscode.debug.startDebugging( - folder, - { - ...template, - ...{ - type: "robotcode", - name: `robotcode: Suite: ${resource}${testcases ? " Testcase: " + testcases : ""}`, - request: "launch", - cwd: folder?.uri.fsPath, - target: uri.fsPath, - args: args, - console: config.get("debug.defaultConsole", "integratedTerminal"), - }, - }, - options - ); + }), + vscode.languages.registerInlineValuesProvider("robotframework", { + provideInlineValues( + document: vscode.TextDocument, + viewPort: vscode.Range, + context: vscode.InlineValueContext, + _token: vscode.CancellationToken + ): vscode.ProviderResult { + const allValues: vscode.InlineValue[] = []; + + for (let l = viewPort.start.line; l <= Math.min(viewPort.end.line, context.stoppedLocation.end.line); l++) { + const line = document.lineAt(l); + const text = line.text.split("#")[0]; + + const variableMatches = + /([$@&%]\{)(?:((?:\d+\.?\d*)|(?:0x[/da-f]+)|(?:0o[0-7]+)|(?:0b[01]+))|(true|false|none|null|empty|space|\/|:|\\n)|((.+?}*)))(\})(?:(\[)(?:(\d+)|(.*?))?(\]))?/gi; + + let match; + while ((match = variableMatches.exec(text))) { + const varName = match[0]; + + const rng = new vscode.Range(l, match.index, l, match.index + varName.length); + allValues.push(new vscode.InlineValueVariableLookup(rng, varName, false)); + } + } + return allValues; + }, + }) + ); + } + + async dispose(): Promise { + this._disposables.dispose(); + } + + static async debugSuiteOrTestcase( + resource: string | vscode.Uri | undefined, + testcases?: string | string[], + options?: vscode.DebugSessionOptions + ): Promise { + if (resource === undefined) return; + + const uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); + + const folder = vscode.workspace.getWorkspaceFolder(uri); + + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); + + const args = []; + + if (testcases) { + if (!(testcases instanceof Array)) { + testcases = [testcases]; + } + for (const testcase of testcases) { + args.push("-t"); + args.push(testcase.toString()); + } } - async attachPython(session: vscode.DebugSession, event: string, options?: any) { - if ( - session.type == "robotcode" && - !session.configuration.noDebug && - session.configuration.attachPython && - options && - options.port - ) { - vscode.debug.startDebugging( - session.workspaceFolder, - { - ...session.configuration.pythonConfiguration, - ...{ - type: "python", - name: `Python ${session.name}`, - request: "attach", - connect: { - port: options.port, - }, - }, - }, - { - parentSession: session, - compact: true, - consoleMode: vscode.DebugConsoleMode.MergeWithParent, - } - ); + const template = config.get("debug.defaultConfiguration", {}); + + vscode.debug.startDebugging( + folder, + { + ...template, + ...{ + type: "robotcode", + name: `robotcode: Suite: ${resource}${testcases ? ` Testcase: ${testcases}` : ""}`, + request: "launch", + cwd: folder?.uri.fsPath, + target: uri.fsPath, + args, + console: config.get("debug.defaultConsole", "integratedTerminal"), + }, + }, + options + ); + } + + static async attachPython(session: vscode.DebugSession, event: string, options?: { port: number }): Promise { + if ( + session.type === "robotcode" && + !session.configuration.noDebug && + session.configuration.attachPython && + options && + options.port + ) { + vscode.debug.startDebugging( + session.workspaceFolder, + { + ...session.configuration.pythonConfiguration, + ...{ + type: "python", + name: `Python ${session.name}`, + request: "attach", + connect: { + port: options.port, + }, + }, + }, + { + parentSession: session, + compact: true, + consoleMode: vscode.DebugConsoleMode.MergeWithParent, } + ); } - async onRobotExited(session: vscode.DebugSession, outputFile?: string, logFile?: string, reportFile?: string) { - if (reportFile) { - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, session.workspaceFolder); - - switch (config.get("run.openReportAfterRun")) { - case "disabled": - return; - case "external": - openExternal(reportFile); - } - } + } + + static async onRobotExited( + session: vscode.DebugSession, + outputFile?: string, + logFile?: string, + reportFile?: string + ): Promise { + if (reportFile) { + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, session.workspaceFolder); + + switch (config.get("run.openReportAfterRun")) { + case "disabled": + return; + case "external": + openExternal(reportFile); + } } + } } diff --git a/vscode-client/extension.ts b/vscode-client/extension.ts index f4bf2ae50..7ba47179a 100644 --- a/vscode-client/extension.ts +++ b/vscode-client/extension.ts @@ -1,115 +1,117 @@ import * as vscode from "vscode"; import { CONFIG_SECTION } from "./config"; import { DebugManager } from "./debugmanager"; -// import { TestHub, testExplorerExtensionId } from "vscode-test-adapter-api"; -// import { TestAdapterRegistrar } from "vscode-test-adapter-util"; -// import { RobotTestAdapter } from "./robottestadapter"; import { LanguageClientsManager } from "./languageclientsmanger"; import { PythonManager } from "./pythonmanger"; import openExternal = require("open"); -// let testHub: TestHub | undefined; - -export async function activateAsync(context: vscode.ExtensionContext) { - const outputChannel = vscode.window.createOutputChannel("RobotCode"); - - outputChannel.appendLine("Activate RobotCode Extension."); - - let pythonManager = new PythonManager(context, outputChannel); - - let languageClientManger = new LanguageClientsManager(context, pythonManager, outputChannel); - - let debugManager = new DebugManager(context, pythonManager, languageClientManger, outputChannel); - - context.subscriptions.push( - pythonManager, - languageClientManger, - debugManager, - - vscode.window.registerTerminalLinkProvider({ - provideTerminalLinks(context: vscode.TerminalLinkContext, token: vscode.CancellationToken) { - if ( - (context.line.startsWith("Log:") || context.line.startsWith("Report:")) && - context.line.endsWith("html") - ) { - let result = /(Log|Report):\s*(?\S.*)/.exec(context.line)?.groups?.link; - - if (result) { - return [ - { - startIndex: context.line.indexOf(result), - length: result.length, - tooltip: "Open report.", - path: result, - }, - ]; - } - } - - return []; - }, - handleTerminalLink(link: any) { - let config = vscode.workspace.getConfiguration( - CONFIG_SECTION, - vscode.workspace.getWorkspaceFolder(vscode.Uri.file(link.path)) - ); - - switch (config.get("run.openReportAfterRun")) { - default: - openExternal(link.path); - break; - } - }, - }), - - vscode.workspace.onDidChangeConfiguration(async (event) => { - for (let s of [ - "robotcode.python", - "robotcode.languageServer.mode", - "robotcode.languageServer.tcpPort", - "robotcode.languageServer.args", - ]) { - if (event.affectsConfiguration(s)) { - await vscode.window - .showWarningMessage( - 'Please use the "Reload Window" action for changes in ' + s + " to take effect.", - ...["Reload Window"] - ) - .then((selection) => { - if (selection === "Reload Window") { - vscode.commands.executeCommand("workbench.action.reloadWindow"); - } - }); - return; - } - } - }) - ); - - // const testExplorerExtension = vscode.extensions.getExtension(testExplorerExtensionId); - // if (testExplorerExtension) { - // testHub = testExplorerExtension.exports; - - // context.subscriptions.push( - // new TestAdapterRegistrar(testHub, (workspacefolder) => new RobotTestAdapter(workspacefolder)) - // ); - // } - - await languageClientManger.refresh(); +class TerminalLink extends vscode.TerminalLink { + constructor(public path: string, startIndex: number, length: number, tooltip?: string) { + super(startIndex, length, tooltip); + } } -function displayProgress(promise: Promise) { - const progressOptions: vscode.ProgressOptions = { - location: vscode.ProgressLocation.Window, - title: "RobotCode extension loading ...", +export async function activateAsync(context: vscode.ExtensionContext): Promise { + const outputChannel = vscode.window.createOutputChannel("RobotCode"); + + outputChannel.appendLine("Activate RobotCode Extension."); + + const pythonManager = new PythonManager(context, outputChannel); + + const languageClientManger = new LanguageClientsManager(context, pythonManager, outputChannel); + + const debugManager = new DebugManager(context, pythonManager, languageClientManger, outputChannel); + + context.subscriptions.push( + pythonManager, + languageClientManger, + debugManager, + + vscode.window.registerTerminalLinkProvider({ + provideTerminalLinks(context: vscode.TerminalLinkContext, _token: vscode.CancellationToken) { + if ((context.line.startsWith("Log:") || context.line.startsWith("Report:")) && context.line.endsWith("html")) { + const result = /(Log|Report):\s*(?\S.*)/.exec(context.line)?.groups?.link; + + if (result) { + return [new TerminalLink(result, context.line.indexOf(result), result.length, "Open report.")]; + } + } + + return []; + }, + handleTerminalLink(link: TerminalLink) { + const config = vscode.workspace.getConfiguration( + CONFIG_SECTION, + vscode.workspace.getWorkspaceFolder(vscode.Uri.file(link.path)) + ); + + switch (config.get("run.openReportAfterRun")) { + default: + openExternal(link.path); + break; + } + }, + }), + + vscode.workspace.onDidChangeConfiguration(async (event) => { + for (const s of [ + "robotcode.python", + "robotcode.languageServer.mode", + "robotcode.languageServer.tcpPort", + "robotcode.languageServer.args", + ]) { + if (event.affectsConfiguration(s)) { + await vscode.window + .showWarningMessage( + 'Please use the "Reload Window" action for changes in ' + s + " to take effect.", + ...["Reload Window"] + ) + .then((selection) => { + if (selection === "Reload Window") { + vscode.commands.executeCommand("workbench.action.reloadWindow"); + } + }); + return; + } + } + }) + ); + + try { + const testController = vscode.test.createTestController("robotcode"); + context.subscriptions.push(testController); + + testController.root.label = "RobotFramework tests"; + + testController.root.canResolveChildren = true; + testController.resolveChildrenHandler = async (item) => { + if (item === testController.root) { + for (const workspace of vscode.workspace.workspaceFolders ?? []) { + const testItem = testController.createTestItem(workspace.uri.toString(), workspace.name, item, workspace.uri); + testItem.debuggable = true; + } + } }; - vscode.window.withProgress(progressOptions, () => promise); + testController.runHandler = async (_request, _token) => { + console.log("hello"); + }; + } catch (e) { + console.log(e); + } + await languageClientManger.refresh(); } -export async function activate(context: vscode.ExtensionContext) { - displayProgress(activateAsync(context)); +function displayProgress(promise: Promise) { + const progressOptions: vscode.ProgressOptions = { + location: vscode.ProgressLocation.Window, + title: "RobotCode extension loading ...", + }; + vscode.window.withProgress(progressOptions, () => promise); } -export async function deactivate() { - //await stopAllClients(); +export async function activate(context: vscode.ExtensionContext): Promise { + displayProgress(activateAsync(context)); } + +// eslint-disable-next-line @typescript-eslint/no-empty-function +export async function deactivate(): Promise {} diff --git a/vscode-client/languageclientsmanger.ts b/vscode-client/languageclientsmanger.ts index 1f5273c6f..b061d855e 100644 --- a/vscode-client/languageclientsmanger.ts +++ b/vscode-client/languageclientsmanger.ts @@ -9,330 +9,288 @@ const LANGUAGE_SERVER_DEFAULT_TCP_PORT = 6610; const LANGUAGE_SERVER_DEFAULT_HOST = "127.0.0.1"; interface Test { - name: string; - source: { - uri: string; - }; - lineNo: number; + name: string; + source: { + uri: string; + }; + lineNo: number; } export class LanguageClientsManager { - private clientsMutex = new Mutex(); - public readonly clients: Map = new Map(); - - private _sortedWorkspaceFolders: string[] | undefined; - private _disposables: vscode.Disposable; - - constructor( - public readonly extensionContext: vscode.ExtensionContext, - public readonly pythonManager: PythonManager, - public readonly outputChannel: vscode.OutputChannel - ) { - this._disposables = vscode.Disposable.from( - this.pythonManager.pythonExtension.exports.settings.onDidChangeExecutionDetails(this.refresh), - vscode.workspace.onDidChangeWorkspaceFolders(async (event) => { - await this.refresh(); - }), - vscode.window.onDidChangeTextEditorSelection(async (event) => { - await this.updateEditorContext(event.textEditor); - }), - vscode.workspace.onDidOpenTextDocument(this.getLanguageClientForDocument) - ); + private clientsMutex = new Mutex(); + + public readonly clients: Map = new Map(); + + private _disposables: vscode.Disposable; + + constructor( + public readonly extensionContext: vscode.ExtensionContext, + public readonly pythonManager: PythonManager, + public readonly outputChannel: vscode.OutputChannel + ) { + this._disposables = vscode.Disposable.from( + this.pythonManager.pythonExtension?.exports.settings.onDidChangeExecutionDetails(this.refresh) ?? { + // eslint-disable-next-line @typescript-eslint/no-empty-function + dispose() {}, + }, + vscode.workspace.onDidChangeWorkspaceFolders(async (_event) => { + await this.refresh(); + }), + vscode.window.onDidChangeTextEditorSelection(async (event) => { + await this.updateEditorContext(event.textEditor); + }), + vscode.workspace.onDidOpenTextDocument(this.getLanguageClientForDocument) + ); + } + + public async stopAllClients(): Promise { + await this.clientsMutex.dispatch(async () => { + const promises: Promise[] = []; + + for (const client of this.clients.values()) { + promises.push(client.stop()); + } + + await Promise.all(promises); + + this.clients.clear(); + }); + } + + async dispose(): Promise { + await this.stopAllClients(); + this._disposables.dispose(); + } + + // eslint-disable-next-line class-methods-use-this + private getServerOptionsTCP(folder: vscode.WorkspaceFolder) { + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); + let port = config.get("languageServer.tcpPort", LANGUAGE_SERVER_DEFAULT_TCP_PORT); + if (port === 0) { + port = LANGUAGE_SERVER_DEFAULT_TCP_PORT; } - - public async stopAllClients() { - await this.clientsMutex.dispatch(async () => { - let promises: Thenable[] = []; - - for (let client of this.clients.values()) { - promises.push(client.stop()); - } - await Promise.all(promises); - - this.clients.clear(); + const serverOptions: ServerOptions = function () { + return new Promise((resolve, reject) => { + const client = new net.Socket(); + client.on("error", (err) => { + reject(err); }); - } - - async dispose() { - await this.stopAllClients(); - this._disposables.dispose(); - } - - private sortedWorkspaceFolders(): string[] { - if (this._sortedWorkspaceFolders === undefined) { - this._sortedWorkspaceFolders = vscode.workspace.workspaceFolders - ? vscode.workspace.workspaceFolders - .map((folder) => { - let result = folder.uri.toString(); - if (result.charAt(result.length - 1) !== "/") { - result = result + "/"; - } - return result; - }) - .sort((a, b) => { - return a.length - b.length; - }) - : []; - } - return this._sortedWorkspaceFolders; - } - - private getOuterMostWorkspaceFolder(folder: vscode.WorkspaceFolder): vscode.WorkspaceFolder { - let sorted = this.sortedWorkspaceFolders(); - for (let element of sorted) { - let uri = folder.uri.toString(); - if (uri.charAt(uri.length - 1) !== "/") { - uri = uri + "/"; - } - if (uri.startsWith(element)) { - return vscode.workspace.getWorkspaceFolder(vscode.Uri.parse(element))!; - } - } - return folder; - } - - private getServerOptionsTCP(folder: vscode.WorkspaceFolder) { - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); - let port = config.get("languageServer.tcpPort", LANGUAGE_SERVER_DEFAULT_TCP_PORT); - if (port === 0) { - port = LANGUAGE_SERVER_DEFAULT_TCP_PORT; - } - const serverOptions: ServerOptions = function () { - return new Promise((resolve, reject) => { - let client = new net.Socket(); - client.on("error", function (err) { - reject(err); - }); - let host = LANGUAGE_SERVER_DEFAULT_HOST; - client.connect(port, host, function () { - resolve({ - reader: client, - writer: client, - }); - }); - }); - }; - return serverOptions; - } - - private getServerOptionsStdIo(folder: vscode.WorkspaceFolder) { - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); - - let pythonCommand = this.pythonManager.getPythonCommand(folder); - - if (!pythonCommand) { - throw new Error("Can't find a valid python executable."); - } + const host = LANGUAGE_SERVER_DEFAULT_HOST; + client.connect(port, host, () => { + resolve({ + reader: client, + writer: client, + }); + }); + }); + }; + return serverOptions; + } - let serverArgs = config.get>("languageServer.args", []); + private getServerOptionsStdIo(folder: vscode.WorkspaceFolder) { + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); - let args: Array = ["-u", this.pythonManager.pythonLanguageServerMain!, "--mode", "stdio"]; + const pythonCommand = this.pythonManager.getPythonCommand(folder); - const serverOptions: ServerOptions = { - command: pythonCommand!, - args: args.concat(serverArgs), - options: { - cwd: folder.uri.fsPath, - // detached: true - }, - }; - return serverOptions; + if (!pythonCommand) { + throw new Error("Can't find a valid python executable."); } - public async getLanguageClientForDocument(document: vscode.TextDocument): Promise { - if (document.languageId !== "robotframework") return; - - return await this.getLanguageClientForResource(document.uri); - } + const serverArgs = config.get>("languageServer.args", []); - public async getLanguageClientForResource(resource: string | vscode.Uri): Promise { - return await this.clientsMutex.dispatch(async () => { - let uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); - let workspaceFolder = vscode.workspace.getWorkspaceFolder(uri); + const args: Array = ["-u", this.pythonManager.pythonLanguageServerMain!, "--mode", "stdio"]; - if (!workspaceFolder) { - return undefined; + const serverOptions: ServerOptions = { + command: pythonCommand!, + args: args.concat(serverArgs), + options: { + cwd: folder.uri.fsPath, + // detached: true + }, + }; + return serverOptions; + } + + public async getLanguageClientForDocument(document: vscode.TextDocument): Promise { + if (document.languageId !== "robotframework") return; + + return await this.getLanguageClientForResource(document.uri); + } + + public async getLanguageClientForResource(resource: string | vscode.Uri): Promise { + return await this.clientsMutex.dispatch(async () => { + const uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri); + + if (!workspaceFolder) { + return undefined; + } + + let result = this.clients.get(workspaceFolder.uri.toString()); + + if (result) return result; + + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, uri); + + const mode = config.get("languageServer.mode", "stdio"); + + const serverOptions: ServerOptions = + mode === "tcp" ? this.getServerOptionsTCP(workspaceFolder) : this.getServerOptionsStdIo(workspaceFolder); + const name = `RobotCode Language Server mode=${mode} for folder "${workspaceFolder.name}"`; + + const outputChannel = mode === "stdio" ? vscode.window.createOutputChannel(name) : undefined; + + const clientOptions: LanguageClientOptions = { + documentSelector: [ + { scheme: "file", language: "robotframework", pattern: `${workspaceFolder.uri.fsPath}/**/*` }, + ], + synchronize: { + configurationSection: [CONFIG_SECTION], + }, + initializationOptions: { + storageUri: this.extensionContext?.storageUri?.toString(), + globalStorageUri: this.extensionContext?.globalStorageUri?.toString(), + }, + diagnosticCollectionName: "robotcode", + workspaceFolder, + outputChannel, + markdown: { + isTrusted: true, + }, + progressOnInitialization: true, + }; + + this.outputChannel.appendLine(`create Language client: ${name}`); + result = new LanguageClient(name, serverOptions, clientOptions); + + this.outputChannel.appendLine(`trying to start Language client: ${name}`); + result.start(); + + result = await result.onReady().then( + async (_) => { + this.outputChannel.appendLine(`client ${result?.clientOptions.workspaceFolder?.uri ?? "unknown"} ready.`); + let counter = 0; + try { + while (!result?.initializeResult && counter < 1000) { + await sleep(10); + counter++; } + } catch { + return undefined; + } + return result; + }, + async (reason) => { + this.outputChannel.appendLine( + `client ${result?.clientOptions.workspaceFolder?.uri ?? "unknown"} error: ${reason}` + ); + vscode.window.showErrorMessage(reason.message ?? "Unknown error."); + return undefined; + } + ); - workspaceFolder = this.getOuterMostWorkspaceFolder(workspaceFolder); - - let result = this.clients.get(workspaceFolder.uri.toString()); - - if (result) return result; - - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, uri); - - let mode = config.get("languageServer.mode", "stdio"); - - const serverOptions: ServerOptions = - mode === "tcp" - ? this.getServerOptionsTCP(workspaceFolder) - : this.getServerOptionsStdIo(workspaceFolder); - let name = `RobotCode Language Server mode=${mode} for folder "${workspaceFolder.name}"`; - - let outputChannel = mode === "stdio" ? vscode.window.createOutputChannel(name) : undefined; - - let clientOptions: LanguageClientOptions = { - documentSelector: [ - { scheme: "file", language: "robotframework", pattern: `${workspaceFolder.uri.fsPath}/**/*` }, - ], - synchronize: { - configurationSection: [CONFIG_SECTION], - }, - initializationOptions: { - storageUri: this.extensionContext?.storageUri?.toString(), - globalStorageUri: this.extensionContext?.globalStorageUri?.toString(), - }, - diagnosticCollectionName: "robotcode", - workspaceFolder: workspaceFolder, - outputChannel: outputChannel, - markdown: { - isTrusted: true, - }, - progressOnInitialization: true, - initializationFailedHandler: (error) => { - return false; - }, - }; - - this.outputChannel.appendLine(`create Language client: ${name}`); - result = new LanguageClient(name, serverOptions, clientOptions); - - this.outputChannel.appendLine(`trying to start Language client: ${name}`); - result.start(); - - result = await result.onReady().then( - async (_) => { - this.outputChannel.appendLine( - `client ${result?.clientOptions.workspaceFolder?.uri ?? "unknown"} ready.` - ); - let counter = 0; - try { - while (!result?.initializeResult && counter < 1000) { - await sleep(10); - counter++; - } - } catch { - return undefined; - } - return result; - }, - async (reason) => { - this.outputChannel.appendLine( - `client ${result?.clientOptions.workspaceFolder?.uri ?? "unknown"} error: ${reason}` - ); - vscode.window.showErrorMessage(reason.message ?? "Unknown error."); - return undefined; - } - ); - - if (result) this.clients.set(workspaceFolder.uri.toString(), result); - - return result; - }); - } + if (result) this.clients.set(workspaceFolder.uri.toString(), result); - public async getCurrentTestFromActiveResource( - resource: string | vscode.Uri | undefined, - selection?: vscode.Selection - ): Promise { - if (resource === undefined) return; + return result; + }); + } - let uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); - let folder = vscode.workspace.getWorkspaceFolder(uri); - if (!folder) return; + public async getCurrentTestFromActiveResource( + resource: string | vscode.Uri | undefined, + selection?: vscode.Selection + ): Promise { + if (resource === undefined) return; - if (!vscode.window.activeTextEditor) return; + const uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); + const folder = vscode.workspace.getWorkspaceFolder(uri); + if (!folder) return undefined; - if (vscode.window.activeTextEditor.document.uri.toString() != uri.toString()) return; + if (!vscode.window.activeTextEditor) return undefined; - if (selection === undefined) selection = vscode.window.activeTextEditor.selection; + if (vscode.window.activeTextEditor.document.uri.toString() !== uri.toString()) return; - let client = await this.getLanguageClientForResource(resource); + if (selection === undefined) selection = vscode.window.activeTextEditor.selection; - if (!client) return; + const client = await this.getLanguageClientForResource(resource); - try { - return ( - (await client?.sendRequest("robotcode/getTestFromPosition", { - textDocument: { uri: uri.toString() }, - position: selection.active, - })) ?? undefined - ); - } catch {} + if (!client) return undefined; - return; - } + return ( + (await client?.sendRequest("robotcode/getTestFromPosition", { + textDocument: { uri: uri.toString() }, + position: selection.active, + })) ?? undefined + ); + } - public async getTestsFromResource(resource: string | vscode.Uri | undefined): Promise { - if (resource === undefined) return []; + public async getTestsFromResource(resource: string | vscode.Uri | undefined): Promise { + if (resource === undefined) return []; - let uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); - let folder = vscode.workspace.getWorkspaceFolder(uri); - if (!folder) return []; + const uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource); + const folder = vscode.workspace.getWorkspaceFolder(uri); + if (!folder) return []; - let client = await this.getLanguageClientForResource(resource); + const client = await this.getLanguageClientForResource(resource); - if (!client) return; + if (!client) return; - let result = - (await client.sendRequest("robotcode/getTests", { - textDocument: { uri: uri.toString() }, - })) ?? undefined; + const result = + (await client.sendRequest("robotcode/getTests", { + textDocument: { uri: uri.toString() }, + })) ?? undefined; - return result; - } + return result; + } - public async getTestFromResource( - resource: string | vscode.Uri | undefined - ): Promise { - let result = await this.getCurrentTestFromActiveResource(resource); - if (!result) { - let tests = await this.getTestsFromResource(resource); - if (tests) { - let items = tests.map((t) => { - return { label: t.name, picked: false, description: "" }; - }); - if (items.length > 0) { - items[0].picked = true; - items[0].description = "(Default)"; - } - let selection = await vscode.window.showQuickPick(items, { title: "Select test(s)" }); - if (selection) { - return selection.label; - } - } + public async getTestFromResource(resource: string | vscode.Uri | undefined): Promise { + const result = await this.getCurrentTestFromActiveResource(resource); + if (!result) { + const tests = await this.getTestsFromResource(resource); + if (tests) { + const items = tests.map((t) => ({ label: t.name, picked: false, description: "" })); + if (items.length > 0) { + items[0].picked = true; + items[0].description = "(Default)"; + } + const selection = await vscode.window.showQuickPick(items, { title: "Select test(s)" }); + if (selection) { + return selection.label; } - return result; + } } + return result; + } - public async updateEditorContext(editor?: vscode.TextEditor) { - if (editor === undefined) editor = vscode.window.activeTextEditor; - - let inTest = false; - if (editor && editor == vscode.window.activeTextEditor && editor.document.languageId == "robotframework") { - let currentTest = await this.getCurrentTestFromActiveResource(editor.document.uri); - inTest = currentTest !== undefined && currentTest !== ""; - } + public async updateEditorContext(editor?: vscode.TextEditor): Promise { + if (editor === undefined) editor = vscode.window.activeTextEditor; - vscode.commands.executeCommand("setContext", "robotCode.editor.inTest", inTest); + let inTest = false; + if (editor && editor === vscode.window.activeTextEditor && editor.document.languageId === "robotframework") { + const currentTest = await this.getCurrentTestFromActiveResource(editor.document.uri); + inTest = currentTest !== undefined && currentTest !== ""; } - public async refresh(uri?: vscode.Uri | undefined) { - await this.clientsMutex.dispatch(async () => { - for (let client of this.clients.values()) { - await client.stop().catch((_) => {}); - } - this.clients.clear(); - }); - - for (let document of vscode.workspace.textDocuments) { - try { - await this.getLanguageClientForDocument(document).catch((_) => {}); - } catch { - // do nothing - } - } - await this.updateEditorContext().catch((_) => {}); + vscode.commands.executeCommand("setContext", "robotCode.editor.inTest", inTest); + } + + public async refresh(_uri?: vscode.Uri | undefined): Promise { + await this.clientsMutex.dispatch(async () => { + for (const client of this.clients.values()) { + // eslint-disable-next-line @typescript-eslint/no-empty-function + await client.stop().catch((_) => {}); + } + this.clients.clear(); + }); + + for (const document of vscode.workspace.textDocuments) { + try { + // eslint-disable-next-line @typescript-eslint/no-empty-function + await this.getLanguageClientForDocument(document).catch((_) => {}); + } catch { + // do nothing + } } + // eslint-disable-next-line @typescript-eslint/no-empty-function + await this.updateEditorContext().catch((_) => {}); + } } diff --git a/vscode-client/pythonmanger.ts b/vscode-client/pythonmanger.ts index 940f7fe9d..72b83530e 100644 --- a/vscode-client/pythonmanger.ts +++ b/vscode-client/pythonmanger.ts @@ -2,60 +2,133 @@ import * as path from "path"; import * as vscode from "vscode"; import { CONFIG_SECTION } from "./config"; +interface PythonExtensionApi { + /** + * Promise indicating whether all parts of the extension have completed loading or not. + * @type {Promise} + * @memberof IExtensionApi + */ + ready: Promise; + jupyter: { + registerHooks(): void; + }; + debug: { + /** + * Generate an array of strings for commands to pass to the Python executable to launch the debugger for remote debugging. + * Users can append another array of strings of what they want to execute along with relevant arguments to Python. + * E.g `['/Users/..../pythonVSCode/pythonFiles/lib/python/debugpy', '--listen', 'localhost:57039', '--wait-for-client']` + * @param {string} host + * @param {number} port + * @param {boolean} [waitUntilDebuggerAttaches=true] + * @returns {Promise} + */ + getRemoteLauncherCommand(host: string, port: number, waitUntilDebuggerAttaches: boolean): Promise; + + /** + * Gets the path to the debugger package used by the extension. + * @returns {Promise} + */ + getDebuggerPackagePath(): Promise; + }; + /** + * Return internal settings within the extension which are stored in VSCode storage + */ + settings: { + /** + * An event that is emitted when execution details (for a resource) change. For instance, when interpreter configuration changes. + */ + readonly onDidChangeExecutionDetails: vscode.Event; + /** + * Returns all the details the consumer needs to execute code within the selected environment, + * corresponding to the specified resource taking into account any workspace-specific settings + * for the workspace to which this resource belongs. + * @param {Resource} [resource] A resource for which the setting is asked for. + * * When no resource is provided, the setting scoped to the first workspace folder is returned. + * * If no folder is present, it returns the global setting. + * @returns {({ execCommand: string[] | undefined })} + */ + getExecutionDetails(resource?: vscode.Uri | undefined): { + /** + * E.g of execution commands returned could be, + * * `['']` + * * `['']` + * * `['conda', 'run', 'python']` which is used to run from within Conda environments. + * or something similar for some other Python environments. + * + * @type {(string[] | undefined)} When return value is `undefined`, it means no interpreter is set. + * Otherwise, join the items returned using space to construct the full execution command. + */ + execCommand: string[] | undefined; + }; + }; +} + export class PythonManager { - public get pythonLanguageServerMain(): string { - return this._pythonLanguageServerMain; - } - public get pythonDebugAdapterMain(): string { - return this._pythonDebugAdapterMain; - } - _pythonLanguageServerMain: string; - _pythonDebugAdapterMain: string; - _pythonExtension: vscode.Extension; - - constructor( - public readonly extensionContext: vscode.ExtensionContext, - public readonly outputChannel: vscode.OutputChannel - ) { - this._pythonLanguageServerMain = this.extensionContext.asAbsolutePath( - path.join("robotcode", "language_server", "__main__.py") - ); - this._pythonDebugAdapterMain = this.extensionContext.asAbsolutePath( - path.join("robotcode", "debug_adapter", "__main__.py") - ); - - this.outputChannel.appendLine("Try to activate Python extension."); + public get pythonLanguageServerMain(): string { + return this._pythonLanguageServerMain; + } + + public get pythonDebugAdapterMain(): string { + return this._pythonDebugAdapterMain; + } + + _pythonLanguageServerMain: string; + _pythonDebugAdapterMain: string; + _pythonExtension: vscode.Extension | undefined; + + constructor( + public readonly extensionContext: vscode.ExtensionContext, + public readonly outputChannel: vscode.OutputChannel + ) { + this._pythonLanguageServerMain = this.extensionContext.asAbsolutePath( + path.join("robotcode", "language_server", "__main__.py") + ); + this._pythonDebugAdapterMain = this.extensionContext.asAbsolutePath( + path.join("robotcode", "debug_adapter", "__main__.py") + ); + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + async dispose(): Promise {} + + get pythonExtension(): vscode.Extension | undefined { + if (!this._pythonExtension) { + this.outputChannel.appendLine("Try to activate python extension"); + try { this._pythonExtension = vscode.extensions.getExtension("ms-python.python")!; - this.outputChannel.appendLine("Try to activate python extension"); + // eslint-disable-next-line @typescript-eslint/no-empty-function this._pythonExtension.activate().then((_) => {}); - this.outputChannel.appendLine("Python Extension is active"); - } - async dispose() {} + this.outputChannel.appendLine("Python Extension is active"); - get pythonExtension(): vscode.Extension { - return this._pythonExtension; + // eslint-disable-next-line @typescript-eslint/no-empty-function + this._pythonExtension.exports.ready.then((_) => {}); + } catch (ex) { + this.outputChannel.appendLine("can't activate python extension"); + } } + return this._pythonExtension; + } - public getPythonCommand(folder: vscode.WorkspaceFolder | undefined): string | undefined { - let config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); - let result: string | undefined = undefined; - - let configPython = config.get("python"); + // eslint-disable-next-line class-methods-use-this + public getPythonCommand(folder: vscode.WorkspaceFolder | undefined): string | undefined { + const config = vscode.workspace.getConfiguration(CONFIG_SECTION, folder); + let result: string | undefined; - if (configPython !== undefined && configPython != "") { - result = configPython; - } else { - const pythonExtension = vscode.extensions.getExtension("ms-python.python")!; - let pythonExtensionPythonPath: string[] | undefined = - pythonExtension?.exports?.settings?.getExecutionDetails(folder?.uri)?.execCommand; + const configPython = config.get("python"); - if (pythonExtensionPythonPath !== undefined) { - result = pythonExtensionPythonPath.join(" "); - } - } + if (configPython !== undefined && configPython !== "") { + result = configPython; + } else { + const pythonExtensionPythonPath: string[] | undefined = + this.pythonExtension?.exports?.settings?.getExecutionDetails(folder?.uri)?.execCommand; - return result; + if (pythonExtensionPythonPath !== undefined) { + result = pythonExtensionPythonPath.join(" "); + } } + + return result; + } } diff --git a/vscode-client/robottestadapter.ts b/vscode-client/robottestadapter.ts deleted file mode 100644 index ebaa9808e..000000000 --- a/vscode-client/robottestadapter.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as vscode from "vscode"; -import { - RetireEvent, - TestAdapter, - TestEvent, - TestLoadFinishedEvent, - TestLoadStartedEvent, - TestRunFinishedEvent, - TestRunStartedEvent, - TestSuiteEvent, - TestSuiteInfo, -} from "vscode-test-adapter-api"; - -export class RobotTestAdapter implements TestAdapter { - private readonly testsEmitter = new vscode.EventEmitter(); - private readonly testStatesEmitter = new vscode.EventEmitter< - TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent - >(); - private readonly autorunEmitter = new vscode.EventEmitter(); - private readonly retireEmitter = new vscode.EventEmitter(); - - get tests(): vscode.Event { - return this.testsEmitter.event; - } - get testStates(): vscode.Event { - return this.testStatesEmitter.event; - } - get autorun(): vscode.Event | undefined { - return this.autorunEmitter.event; - } - get retire(): vscode.Event | undefined { - return this.retireEmitter.event; - } - - constructor(public readonly workspace: vscode.WorkspaceFolder) {} - - async load() { - this.testsEmitter.fire({ type: "started" }); - - let tests: TestSuiteInfo = { - type: "suite", - id: "1", - label: this.workspace.name, - description: "Hello World", - tooltip: "rumba", - debuggable: true, - - children: [ - { - type: "suite", - id: "2", - label: "rumba", - children: [ - { - type: "test", - id: "3", - label: "first test", - debuggable: true - } - ] - - } - ], - }; - - this.testsEmitter.fire({ type: "finished", suite: tests }); - } - async run(tests: string[]): Promise { - throw new Error("Method not implemented."); - } - cancel(): void { - throw new Error("Method not implemented."); - } - dispose(): void { - throw new Error("Method not implemented."); - } -} diff --git a/vscode-client/test/runTest.ts b/vscode-client/test/runTest.ts index 1eabfa33d..33fb43f85 100644 --- a/vscode-client/test/runTest.ts +++ b/vscode-client/test/runTest.ts @@ -1,23 +1,23 @@ -import * as path from 'path'; +import * as path from "path"; -import { runTests } from 'vscode-test'; +import { runTests } from "vscode-test"; async function main() { - try { - // The folder containing the Extension Manifest package.json - // Passed to `--extensionDevelopmentPath` - const extensionDevelopmentPath = path.resolve(__dirname, '../../'); + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, "../../"); - // The path to test runner - // Passed to --extensionTestsPath - const extensionTestsPath = path.resolve(__dirname, './suite/index'); + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, "./suite/index"); - // Download VS Code, unzip it and run the integration test - await runTests({ extensionDevelopmentPath, extensionTestsPath }); - } catch (err) { - console.error('Failed to run tests'); - process.exit(1); - } + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + console.error("Failed to run tests"); + process.exit(1); + } } main(); diff --git a/vscode-client/test/suite/extension.test.ts b/vscode-client/test/suite/extension.test.ts index 08a6f78f2..9b720d746 100644 --- a/vscode-client/test/suite/extension.test.ts +++ b/vscode-client/test/suite/extension.test.ts @@ -1,15 +1,15 @@ -import * as assert from 'assert'; +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 vscode from "vscode"; // import * as myExtension from '../../extension'; -suite('Extension Test Suite', () => { - vscode.window.showInformationMessage('Start all tests.'); +suite("Extension Test Suite", () => { + vscode.window.showInformationMessage("Start all tests."); - test('Sample test', () => { - assert.equal(-1, [1, 2, 3].indexOf(5)); - assert.equal(-1, [1, 2, 3].indexOf(0)); - }); + test("Sample test", () => { + assert.equal(-1, [1, 2, 3].indexOf(5)); + assert.equal(-1, [1, 2, 3].indexOf(0)); + }); }); diff --git a/vscode-client/test/suite/index.ts b/vscode-client/test/suite/index.ts index 7029e38ed..c1f80ff3b 100644 --- a/vscode-client/test/suite/index.ts +++ b/vscode-client/test/suite/index.ts @@ -1,38 +1,38 @@ -import * as path from 'path'; -import * as Mocha from 'mocha'; -import * as glob from 'glob'; +import * as path from "path"; +import * as Mocha from "mocha"; +import * as glob from "glob"; export function run(): Promise { - // Create the mocha test - const mocha = new Mocha({ - ui: 'tdd', - color: true - }); + // Create the mocha test + const mocha = new Mocha({ + ui: "tdd", + color: true, + }); - const testsRoot = path.resolve(__dirname, '..'); + const testsRoot = path.resolve(__dirname, ".."); - return new Promise((c, e) => { - glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { - if (err) { - return e(err); - } + return new Promise((resolve, reject) => { + glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { + if (err) { + return reject(err); + } - // Add files to the test suite - files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + // Add files to the test suite + files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); - try { - // Run the mocha test - mocha.run(failures => { - if (failures > 0) { - e(new Error(`${failures} tests failed.`)); - } else { - c(); - } - }); - } catch (err) { - console.error(err); - e(err); - } - }); - }); + try { + // Run the mocha test + mocha.run((failures) => { + if (failures > 0) { + reject(new Error(`${failures} tests failed.`)); + } else { + resolve(); + } + }); + } catch (ex) { + console.error(ex); + reject(ex); + } + }); + }); } diff --git a/vscode-client/utils.ts b/vscode-client/utils.ts index 423ad9866..1ac26cd38 100644 --- a/vscode-client/utils.ts +++ b/vscode-client/utils.ts @@ -1,48 +1,47 @@ export async function sleep(timeout: number): Promise { - return new Promise((resolve) => { - setTimeout(() => resolve(timeout), timeout); - }); + return new Promise((resolve) => { + setTimeout(() => resolve(timeout), timeout); + }); } export async function waitForPromise(promise: Promise, timeout: number): Promise { - // Set a timer that will resolve with null - return new Promise((resolve, reject) => { - const timer = setTimeout(() => resolve(null), timeout); - promise - .then((result) => { - // When the promise resolves, make sure to clear the timer or - // the timer may stick around causing tests to wait - clearTimeout(timer); - resolve(result); - }) - .catch((e) => { - clearTimeout(timer); - reject(e); - }); - }); + // Set a timer that will resolve with null + return new Promise((resolve, reject) => { + const timer = setTimeout(() => resolve(null), timeout); + promise + .then((result) => { + // When the promise resolves, make sure to clear the timer or + // the timer may stick around causing tests to wait + clearTimeout(timer); + resolve(result); + }) + .catch((e) => { + clearTimeout(timer); + reject(e); + }); + }); } export class Mutex { - private mutex = Promise.resolve(); + private mutex = Promise.resolve(); - lock(): PromiseLike<() => void> { - let begin: (unlock: () => void) => void = (unlock) => {}; + lock(): PromiseLike<() => void> { + // eslint-disable-next-line @typescript-eslint/no-empty-function + let begin: (unlock: () => void) => void = (_unlock) => {}; - this.mutex = this.mutex.then(() => { - return new Promise(begin); - }); + this.mutex = this.mutex.then(() => new Promise(begin)); - return new Promise((res) => { - begin = res; - }); - } + return new Promise((resolve) => { + begin = resolve; + }); + } - async dispatch(fn: (() => T) | (() => PromiseLike)): Promise { - const unlock = await this.lock(); - try { - return await Promise.resolve(fn()).finally(() => unlock()); - } finally { - unlock(); - } + async dispatch(fn: (() => T) | (() => PromiseLike)): Promise { + const unlock = await this.lock(); + try { + return await Promise.resolve(fn()).finally(() => unlock()); + } finally { + unlock(); } + } } diff --git a/webpack.config.js b/webpack.config.js index ed4064cd5..92ccf2b54 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,29 +1,27 @@ -//@ts-check +"use strict"; -'use strict'; +const path = require("path"); -const path = require('path'); - -/**@type {import('webpack').Configuration}*/ +/** @type {import('webpack').Configuration} */ const config = { - target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ + target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ - entry: './vscode-client/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + entry: "./vscode-client/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ - path: path.resolve(__dirname, 'out'), - filename: 'extension.js', - libraryTarget: 'commonjs2', - devtoolModuleFilenameTemplate: '../[resource-path]', - clean: true + path: path.resolve(__dirname, "out"), + filename: "extension.js", + libraryTarget: "commonjs2", + devtoolModuleFilenameTemplate: "../[resource-path]", + clean: true, }, - devtool: 'source-map', + devtool: "source-map", externals: { - vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ + vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ }, resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader - extensions: ['.ts', '.js'] + extensions: [".ts", ".js"], }, module: { rules: [ @@ -32,11 +30,11 @@ const config = { exclude: /node_modules/, use: [ { - loader: 'ts-loader' - } - ] - } - ] - } + loader: "ts-loader", + }, + ], + }, + ], + }, }; -module.exports = config; \ No newline at end of file +module.exports = config;