diff --git a/.github/classifier.yml b/.github/classifier.yml index 8acb0002347..c0c716d2526 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -15,7 +15,7 @@ css-less-scss: [], debug-console: [], debug: { - assignees: [ weinand ], + assignees: [ isidorn ], assignLabel: false }, diff-editor: : { diff --git a/.github/commands.yml b/.github/commands.yml index 4bc649c91b6..6344ec67692 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -18,24 +18,28 @@ { type: 'label', name: '*dev-question', + allowTriggerByBot: true, action: 'close', comment: "We have a great developer community [over on slack](https://aka.ms/vscode-dev-community) where extension authors help each other. This is a great place for you to ask questions and find support.\n\nHappy Coding!" }, { type: 'label', name: '*extension-candidate', + allowTriggerByBot: true, action: 'close', comment: "We try to keep VS Code lean and we think the functionality you're asking for is great for a VS Code extension. Maybe you can already find one that suits you in the [VS Code Marketplace](https://aka.ms/vscodemarketplace). Just in case, in a few simple steps you can get started [writing your own extension](https://aka.ms/vscodewritingextensions). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" }, { type: 'label', name: '*not-reproducible', + allowTriggerByBot: true, action: 'close', comment: "We closed this issue because we are unable to reproduce the problem with the steps you describe. Chances are we've already fixed your problem in a recent version of VS Code. If not, please ask us to reopen the issue and provide us with more detail. Our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines might help you with that.\n\nHappy Coding!" }, { type: 'label', name: '*out-of-scope', + allowTriggerByBot: true, action: 'close', comment: "This issue is being closed to keep the number of issues in our inbox on a manageable level, we are closing issues that are not going to be addressed in the foreseeable future: We look at the number of votes the issue has received and the number of duplicate issues filed. More details [here](https://aka.ms/vscode-out-of-scope). If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nThanks for your understanding and happy coding!" }, @@ -56,12 +60,14 @@ { type: 'label', name: '*as-designed', + allowTriggerByBot: true, action: 'close', comment: "The described behavior is how it is expected to work. If you disagree, please explain what is expected and what is not in more detail. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" }, { type: 'label', name: '*english-please', + allowTriggerByBot: true, action: 'close', comment: "This issue is being closed because its description is not in English, that makes it hard for us to work on it. Please open a new issue with an English description. You might find [Bing Translator](https://www.bing.com/translator) useful." }, diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..276121a227b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ + + +This PR fixes # diff --git a/.yarnrc b/.yarnrc index ff946c7a250..c54f7d6d6e3 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "4.2.10" +target "6.0.12" runtime "electron" diff --git a/README.md b/README.md index 5743ba55e8d..5d39d7e40c6 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ please see the document [How to Contribute](https://github.com/Microsoft/vscode/ * [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#debugging) * [Coding guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) * [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) +* [Finding an issue to work on](https://github.com/microsoft/vscode/wiki/How-to-Contribute#where-to-contribute) * [Contributing to translations](https://aka.ms/vscodeloc) ## Feedback diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index aa56c32d53d..6fe67025875 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -27,7 +27,7 @@ This project incorporates components from the projects listed below. The origina 20. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) 21. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) 22. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) -23. jeff-hykin/cpp-textmate-grammar version 1.13.2 (https://github.com/jeff-hykin/cpp-textmate-grammar) +23. jeff-hykin/cpp-textmate-grammar version 1.14.6 (https://github.com/jeff-hykin/cpp-textmate-grammar) 24. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) 25. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) 26. language-docker (https://github.com/moby/moby) @@ -65,7 +65,7 @@ This project incorporates components from the projects listed below. The origina 58. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage) 59. Unicode version 12.0.0 (http://www.unicode.org/) 60. vscode-logfile-highlighter version 2.4.1 (https://github.com/emilast/vscode-logfile-highlighter) -61. vscode-octicons-font version 1.3.1 (https://github.com/Microsoft/vscode-octicons-font) +61. vscode-octicons-font version 1.3.2 (https://github.com/Microsoft/vscode-octicons-font) 62. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) 63. Web Background Synchronization (https://github.com/WICG/BackgroundSync) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 76a1e1e51fc..bedc2d3ed52 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.39.0", + "version": "1.39.1", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 7cbda83c0d8..9d3a7caa07e 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -43,7 +43,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@EXEC@@', `/usr/share/${product.applicationName}/${product.applicationName}`)) - .pipe(replace('@@ICON@@', product.linuxIconName)) + .pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.linuxIconName}.png`)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) diff --git a/build/package.json b/build/package.json index bf3ec5ce3f9..84556e6e17d 100644 --- a/build/package.json +++ b/build/package.json @@ -40,7 +40,7 @@ "mime": "^1.3.4", "minimist": "^1.2.0", "request": "^2.85.0", - "terser": "4.3.1", + "terser": "4.3.8", "tslint": "^5.9.1", "typescript": "3.6.2", "vsce": "1.48.0", diff --git a/build/yarn.lock b/build/yarn.lock index 937eb1daa43..2451df9b537 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2178,10 +2178,10 @@ terser@*: source-map "~0.6.1" source-map-support "~0.5.12" -terser@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.1.tgz#09820bcb3398299c4b48d9a86aefc65127d0ed65" - integrity sha512-pnzH6dnFEsR2aa2SJaKb1uSCl3QmIsJ8dEkj0Fky+2AwMMcC9doMqLOQIH6wVTEKaVfKVvLSk5qxPBEZT9mywg== +terser@4.3.8: + version "4.3.8" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.8.tgz#707f05f3f4c1c70c840e626addfdb1c158a17136" + integrity sha512-otmIRlRVmLChAWsnSFNO0Bfk6YySuBp6G9qrHiJwlLDd4mxe2ta4sjI7TzIR+W1nBMjilzrMcPOz9pSusgx3hQ== dependencies: commander "^2.20.0" source-map "~0.6.1" diff --git a/cgmanifest.json b/cgmanifest.json index d26f37b6204..c45dca3ef5c 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "c6a08e5368de4352903e702cde750b33239a50ab" + "commitHash": "91f08db83c2ce8c722ddf0911ead8f7c473bedfa" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "69.0.3497.128" + "version": "76.0.3809.146" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "8c70b2084ce5f76ea1e3b3c4ccdeee4483fe338b" + "commitHash": "64219741218aa87e259cf8257596073b8e747f0a" } }, "isOnlyProductionDependency": true, - "version": "10.11.0" + "version": "12.4.0" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "4e4c7527c63fcf27dffaeb58bde996b8d859c0ed" + "commitHash": "1e50380fab37f407c4d357e1e30ecbc3d5a703b8" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "4.2.10" + "version": "6.0.12" }, { "component": { @@ -98,11 +98,11 @@ "git": { "name": "vscode-octicons-font", "repositoryUrl": "https://github.com/Microsoft/vscode-octicons-font", - "commitHash": "415cd5b42ab699b6b46c0bf011ada0a2ae50bfb4" + "commitHash": "4cbf2bd35cf0084eabd47d322cc58339fd7743cf" } }, "license": "MIT", - "version": "1.3.1" + "version": "1.3.2" }, { "component": { diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index e6658a6d425..bd10c038f8a 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -37,7 +37,8 @@ "launch.json", "tasks.json", "keybindings.json", - "extensions.json" + "extensions.json", + "argv.json" ] } ], @@ -71,8 +72,8 @@ "url": "vscode://schemas/workspaceConfig" }, { - "fileMatch": "%APP_SETTINGS_HOME%/locale.json", - "url": "vscode://schemas/locale" + "fileMatch": "**/argv.json", + "url": "vscode://schemas/argv" }, { "fileMatch": "/.vscode/settings.json", @@ -105,6 +106,10 @@ { "fileMatch": "/.devcontainer.json", "url": "./schemas/devContainer.schema.json" + }, + { + "fileMatch": "%APP_SETTINGS_HOME%/globalStorage/ms-vscode-remote.remote-containers/imageConfigs/*.json", + "url": "./schemas/attachContainer.schema.json" } ] }, diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json new file mode 100644 index 00000000000..5f3d28abb89 --- /dev/null +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/schema#", + "description": "Configures an attached to container", + "allowComments": true, + "type": "object", + "definitions": { + "attachContainer": { + "type": "object", + "properties": { + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer" + } + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string" + } + } + } + } + }, + "allOf": [ + { + "$ref": "#/definitions/attachContainer" + } + ] +} diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts index aff533d86d9..72e2fcf627f 100644 --- a/extensions/configuration-editing/src/extension.ts +++ b/extensions/configuration-editing/src/extension.ts @@ -75,7 +75,8 @@ function registerVariableCompletions(pattern: string): vscode.Disposable { { label: 'fileDirname', detail: localize('fileDirname', "The current opened file's dirname") }, { label: 'fileExtname', detail: localize('fileExtname', "The current opened file's extension") }, { label: 'fileBasename', detail: localize('fileBasename', "The current opened file's basename") }, - { label: 'fileBasenameNoExtension', detail: localize('fileBasenameNoExtension', "The current opened file's basename with no file extension") } + { label: 'fileBasenameNoExtension', detail: localize('fileBasenameNoExtension', "The current opened file's basename with no file extension") }, + { label: 'defaultBuildTask', detail: localize('defaultBuildTask', "The name of the default build task. If there is not a single default build task then a quick pick is shown to choose the build task.") }, ].map(variable => ({ label: '${' + variable.label + '}', range: new vscode.Range(startPosition, position), diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index 88107685266..d8698b46c09 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -13,8 +13,7 @@ ["[", "]"], ["(", ")"], { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, - { "open": "/*", "close": " */", "notIn": ["string"] } + { "open": "\"", "close": "\"", "notIn": ["string", "comment"] } ], "surroundingPairs": [ ["{", "}"], @@ -30,4 +29,4 @@ "end": "^\\s*#endregion\\b" } } -} \ No newline at end of file +} diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 1dbd7608495..6837c63d401 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -308,6 +308,18 @@ "order": 24, "title": "%scss.title%", "properties": { + "scss.completion.triggerPropertyValueCompletion": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%scss.completion.triggerPropertyValueCompletion.desc%" + }, + "scss.completion.completePropertyWithSemicolon": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%scss.completion.completePropertyWithSemicolon.desc%" + }, "scss.validate": { "type": "boolean", "scope": "resource", @@ -530,6 +542,18 @@ "type": "object", "title": "%less.title%", "properties": { + "less.completion.triggerPropertyValueCompletion": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%less.completion.triggerPropertyValueCompletion.desc%" + }, + "less.completion.completePropertyWithSemicolon": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%less.completion.completePropertyWithSemicolon.desc%" + }, "less.validate": { "type": "boolean", "scope": "resource", @@ -759,11 +783,11 @@ ] }, "dependencies": { - "vscode-languageclient": "^5.3.0-next.6", + "vscode-languageclient": "^6.0.0-next.1", "vscode-nls": "^4.1.1" }, "devDependencies": { "@types/node": "^10.14.8", - "mocha": "^5.2.0" + "mocha": "^6.1.4" } } diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index 550820976d7..30220ccf940 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -4,7 +4,7 @@ "css.title": "CSS", "css.customData.desc": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/Microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", "css.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", - "css.completion.completePropertyWithSemicolon.desc": "Insert semicolon at end when completing CSS properties", + "css.completion.completePropertyWithSemicolon.desc": "Insert semicolon at end of line when completing CSS properties", "css.lint.argumentsInColorFunction.desc": "Invalid number of parameters.", "css.lint.boxModel.desc": "Do not use `width` or `height` when using `padding` or `border`.", "css.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties.", @@ -29,6 +29,8 @@ "css.validate.title": "Controls CSS validation and problem severities.", "css.validate.desc": "Enables or disables all validations.", "less.title": "LESS", + "less.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", + "less.completion.completePropertyWithSemicolon.desc": "Insert semicolon at end of line when completing CSS properties", "less.lint.argumentsInColorFunction.desc": "Invalid number of parameters.", "less.lint.boxModel.desc": "Do not use `width` or `height` when using `padding` or `border`.", "less.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties.", @@ -51,6 +53,8 @@ "less.validate.title": "Controls LESS validation and problem severities.", "less.validate.desc": "Enables or disables all validations.", "scss.title": "SCSS (Sass)", + "scss.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", + "scss.completion.completePropertyWithSemicolon.desc": "Insert semicolon at end of line when completing CSS properties", "scss.lint.argumentsInColorFunction.desc": "Invalid number of parameters.", "scss.lint.boxModel.desc": "Do not use `width` or `height` when using `padding` or `border`.", "scss.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties.", diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index def7def57c2..a263ed94c2e 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,15 +9,15 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.8", - "vscode-languageserver": "^5.3.0-next.8" + "vscode-css-languageservice": "^4.0.3-next.11", + "vscode-languageserver": "^6.0.0-next.1" }, "devDependencies": { "@types/mocha": "2.2.33", "@types/node": "^10.14.8", "glob": "^7.1.4", "mocha": "^6.1.4", - "mocha-junit-reporter": "^1.23.0", + "mocha-junit-reporter": "^1.23.1", "mocha-multi-reporters": "^1.1.7" }, "scripts": { diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index a1dee46c523..7cf07998b1e 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -6,7 +6,7 @@ import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder } from 'vscode-languageserver'; -import URI from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { TextDocument, CompletionList, Position } from 'vscode-languageserver-types'; import { stat as fsStat } from 'fs'; @@ -75,9 +75,9 @@ const fileSystemProvider: FileSystemProvider = { let type = FileType.Unknown; if (stats.isFile()) { type = FileType.File; - } else if (stats.isDirectory) { + } else if (stats.isDirectory()) { type = FileType.Directory; - } else if (stats.isSymbolicLink) { + } else if (stats.isSymbolicLink()) { type = FileType.SymbolicLink; } @@ -390,4 +390,4 @@ connection.onSelectionRanges((params, token) => { // Listen on the connection -connection.listen(); \ No newline at end of file +connection.listen(); diff --git a/extensions/css-language-features/server/src/pathCompletion.ts b/extensions/css-language-features/server/src/pathCompletion.ts index aafed82b513..6862f28e034 100644 --- a/extensions/css-language-features/server/src/pathCompletion.ts +++ b/extensions/css-language-features/server/src/pathCompletion.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import * as fs from 'fs'; -import URI from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types'; import { WorkspaceFolder } from 'vscode-languageserver'; diff --git a/extensions/css-language-features/server/src/test/completion.test.ts b/extensions/css-language-features/server/src/test/completion.test.ts index 3a832600355..d8230ba2922 100644 --- a/extensions/css-language-features/server/src/test/completion.test.ts +++ b/extensions/css-language-features/server/src/test/completion.test.ts @@ -5,7 +5,7 @@ import 'mocha'; import * as assert from 'assert'; import * as path from 'path'; -import Uri from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { TextDocument, CompletionList } from 'vscode-languageserver-types'; import { WorkspaceFolder } from 'vscode-languageserver-protocol'; import { getPathCompletionParticipant } from '../pathCompletion'; @@ -60,8 +60,8 @@ suite('Completions', () => { } test('CSS url() Path completion', function () { - let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); - let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; + let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); + let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }]; assertCompletions('html { background-image: url("./|")', { items: [ @@ -119,8 +119,8 @@ suite('Completions', () => { }); test('CSS url() Path Completion - Unquoted url', function () { - let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); - let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; + let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); + let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }]; assertCompletions('html { background-image: url(./|)', { items: [ @@ -148,8 +148,8 @@ suite('Completions', () => { }); test('CSS @import Path completion', function () { - let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); - let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; + let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); + let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }]; assertCompletions(`@import './|'`, { items: [ @@ -171,8 +171,8 @@ suite('Completions', () => { * For SCSS, `@import 'foo';` can be used for importing partial file `_foo.scss` */ test('SCSS @import Path completion', function () { - let testCSSUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); - let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; + let testCSSUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); + let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }]; /** * We are in a CSS file, so no special treatment for SCSS partial files @@ -184,7 +184,7 @@ suite('Completions', () => { ] }, testCSSUri, folders); - let testSCSSUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/scss/main.scss')).toString(); + let testSCSSUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/scss/main.scss')).toString(); assertCompletions(`@import './|'`, { items: [ { label: '_foo.scss', resultText: `@import './foo'` } @@ -193,12 +193,12 @@ suite('Completions', () => { }); test('Completion should ignore files/folders starting with dot', function () { - let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); - let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; + let testUri = URI.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); + let folders = [{ name: 'x', uri: URI.file(path.resolve(__dirname, '../../test')).toString() }]; assertCompletions('html { background-image: url("../|")', { count: 4 }, testUri, folders); }); -}); \ No newline at end of file +}); diff --git a/extensions/css-language-features/server/src/test/links.test.ts b/extensions/css-language-features/server/src/test/links.test.ts index bce7a11bc8a..a2a13361016 100644 --- a/extensions/css-language-features/server/src/test/links.test.ts +++ b/extensions/css-language-features/server/src/test/links.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'mocha'; import * as assert from 'assert'; -import Uri from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { resolve } from 'path'; import { TextDocument, DocumentLink } from 'vscode-languageserver-types'; import { WorkspaceFolder } from 'vscode-languageserver-protocol'; @@ -54,7 +54,7 @@ suite('Links', () => { } function getTestResource(path: string) { - return Uri.file(resolve(__dirname, '../../test/linksTestFixtures', path)).toString(); + return URI.file(resolve(__dirname, '../../test/linksTestFixtures', path)).toString(); } test('url links', function () { @@ -76,4 +76,4 @@ suite('Links', () => { [{ offset: 29, value: '"~foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders ); }); -}); \ No newline at end of file +}); diff --git a/extensions/css-language-features/server/src/utils/documentContext.ts b/extensions/css-language-features/server/src/utils/documentContext.ts index 494ff395e9d..7c488a4e372 100644 --- a/extensions/css-language-features/server/src/utils/documentContext.ts +++ b/extensions/css-language-features/server/src/utils/documentContext.ts @@ -7,7 +7,7 @@ import { DocumentContext } from 'vscode-css-languageservice'; import { endsWith, startsWith } from '../utils/strings'; import * as url from 'url'; import { WorkspaceFolder } from 'vscode-languageserver'; -import URI from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { join, dirname } from 'path'; import { existsSync } from 'fs'; diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 9cda831e23b..6d38104e2e5 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -479,10 +479,10 @@ mkdirp@0.5.1, mkdirp@~0.5.1: dependencies: minimist "0.0.8" -mocha-junit-reporter@^1.23.0: - version "1.23.0" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.0.tgz#c5ad7f10b5aa9a7cc6e169b6bf15baf2700266ca" - integrity sha512-pmpnEO4iDTmLfrT2RKqPsc5relG4crnDSGmXPuGogdda27A7kLujDNJV4EbTbXlVBCZXggN9rQYPEWMkOv4AAA== +mocha-junit-reporter@^1.23.1: + version "1.23.1" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.1.tgz#ba11519c0b967f404e4123dd69bc4ba022ab0f12" + integrity sha512-qeDvKlZyAH2YJE1vhryvjUQ06t2hcnwwu4k5Ddwn0GQINhgEYFhlGM0DwYCVUHq5cuo32qAW6HDsTHt7zz99Ng== dependencies: debug "^2.2.0" md5 "^2.1.0" @@ -781,41 +781,40 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^4.0.3-next.8: - version "4.0.3-next.8" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.8.tgz#0b81693b6ea9d10f78775a1dcad2c0f464fbde16" - integrity sha512-agBPPu86bPKIK5v6CFnWeBXN4jvnCzc67GZa/pvrIWeRdG7nvTu5Y2wYdwdesdpWzno9/5tfFEPp0KJbKQ4l+A== +vscode-css-languageservice@^4.0.3-next.11: + version "4.0.3-next.11" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.11.tgz#b353ae30551c052e7133f6640d5f4d4755c27fb0" + integrity sha512-6rdruqzWkAbGkTw56WTMS7SmhOYIZ4Dnrs3Wc64cn/saF1f+ib76EUvISiYLZnc+S9FsZu2lnlzPeeJ3LItm2A== dependencies: - vscode-languageserver-types "^3.15.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" vscode-nls "^4.1.1" vscode-uri "^2.0.3" -vscode-jsonrpc@^4.1.0-next.2: - version "4.1.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" - integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== +vscode-jsonrpc@^5.0.0-next.2: + version "5.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" + integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== -vscode-languageserver-protocol@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" - integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== +vscode-languageserver-protocol@^3.15.0-next.9: + version "3.15.0-next.9" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz#e768256bd5b580b25bfbc8099bc03bc4c42ebf30" + integrity sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g== dependencies: - vscode-jsonrpc "^4.1.0-next.2" - vscode-languageserver-types "^3.15.0-next.2" + vscode-jsonrpc "^5.0.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" -vscode-languageserver-types@^3.15.0-next.2: - version "3.15.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" - integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== +vscode-languageserver-types@^3.15.0-next.5: + version "3.15.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b" + integrity sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw== -vscode-languageserver@^5.3.0-next.8: - version "5.3.0-next.8" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.8.tgz#12a4adf60374dbb93e153e08bdca5525f9b2029f" - integrity sha512-6vUb96wsRfrFqndril3gct/FBCSc24OxFZ2iz7kuEuXvLaIcEVOcSZIqQK8oFN7PdbAIaa9nnIpKSy4Yd15cIw== +vscode-languageserver@^6.0.0-next.1: + version "6.0.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.0.0-next.1.tgz#4d71886d4a17d22eafc61b3a5fbf84e8e27c191f" + integrity sha512-LSF6bXoFeXfMPRNyqzI3yFX/kD2DzXBemqvyj1kDWNVraiWttm4xKF4YXsvJ7Z3s9sVt/Dpu3CFU3w61PGNZMg== dependencies: - vscode-languageserver-protocol "^3.15.0-next.6" + vscode-languageserver-protocol "^3.15.0-next.9" vscode-textbuffer "^1.0.0" - vscode-uri "^1.0.6" vscode-nls@^4.1.1: version "4.1.1" @@ -827,11 +826,6 @@ vscode-textbuffer@^1.0.0: resolved "https://registry.yarnpkg.com/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz#1faee638c8e0e4131c8d5c353993a1874acda086" integrity sha512-zPaHo4urgpwsm+PrJWfNakolRpryNja18SUip/qIIsfhuEqEIPEXMxHOlFPjvDC4JgTaimkncNW7UMXRJTY6ow== -vscode-uri@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" - integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== - vscode-uri@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index 48f16a42fe5..cd8d040e9a3 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -7,6 +7,35 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -25,42 +54,143 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -debug@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: - ms "2.0.0" + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -escape-string-regexp@1.0.5: +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +es-abstract@^1.5.1: + version "1.14.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" + integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.0" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.0.0" + string.prototype.trimright "^2.0.0" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -79,10 +209,22 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== inflight@^1.0.4: version "1.0.6" @@ -97,6 +239,73 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +is-buffer@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -116,27 +325,80 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" -mocha@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== +mocha@^6.1.4: + version "6.2.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.1.tgz#da941c99437da9bac412097859ff99543969f94c" + integrity sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A== dependencies: + ansi-colors "3.2.3" browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" + debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" - glob "7.1.2" + find-up "3.0.0" + glob "7.1.3" growl "1.10.5" - he "1.1.1" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" minimatch "3.0.4" mkdirp "0.5.1" - supports-color "5.4.0" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.0" + yargs-parser "13.1.1" + yargs-unparser "1.6.0" -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +object-inspect@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" once@^1.3.0: version "1.4.0" @@ -145,55 +407,229 @@ once@^1.3.0: dependencies: wrappy "1" +p-limit@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" + integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -semver@^5.5.0: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" - integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string.prototype.trimleft@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== dependencies: has-flag "^3.0.0" -vscode-jsonrpc@^4.1.0-next.2: - version "4.1.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" - integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== - -vscode-languageclient@^5.3.0-next.6: - version "5.3.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.3.0-next.6.tgz#35e74882781158e8b111911c0953869d3df08777" - integrity sha512-DxT8+gkenjCjJV6ArcP75/AQfx6HP6m6kHIbacPCpffMeoE1YMLKj6ZixA9J87yr0fMtBmqumLmDeGe7MIF2bw== +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: - semver "^5.5.0" - vscode-languageserver-protocol "^3.15.0-next.6" + has-flag "^3.0.0" -vscode-languageserver-protocol@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" - integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== +vscode-jsonrpc@^5.0.0-next.2: + version "5.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" + integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== + +vscode-languageclient@^6.0.0-next.1: + version "6.0.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.0.0-next.1.tgz#deca1743afd20da092e04e40ef73cedbbd978455" + integrity sha512-eJ9VjLFNINArgRzLbQ11YlWry7dM93GEODkQBXTRfrSypksiO9qSGr4SHhWgxxP26p4FRSpzc/17+N+Egnnchg== dependencies: - vscode-jsonrpc "^4.1.0-next.2" - vscode-languageserver-types "^3.15.0-next.2" + semver "^6.3.0" + vscode-languageserver-protocol "^3.15.0-next.9" -vscode-languageserver-types@^3.15.0-next.2: - version "3.15.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" - integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== +vscode-languageserver-protocol@^3.15.0-next.9: + version "3.15.0-next.9" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz#e768256bd5b580b25bfbc8099bc03bc4c42ebf30" + integrity sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g== + dependencies: + vscode-jsonrpc "^5.0.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" + +vscode-languageserver-types@^3.15.0-next.5: + version "3.15.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b" + integrity sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw== vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@13.1.1, yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.0, yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" diff --git a/extensions/git/package.json b/extensions/git/package.json index b64dfb4707e..eafc4916439 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -362,6 +362,11 @@ "title": "%command.ignore%", "category": "Git" }, + { + "command": "git.revealInExplorer", + "title": "%command.revealInExplorer%", + "category": "Git" + }, { "command": "git.stashIncludeUntracked", "title": "%command.stashIncludeUntracked%", @@ -507,6 +512,10 @@ "command": "git.restoreCommitTemplate", "when": "false" }, + { + "command": "git.revealInExplorer", + "when": "false" + }, { "command": "git.undoCommit", "when": "config.git.enabled && gitOpenRepositoryCount != 0" @@ -913,6 +922,11 @@ "when": "scmProvider == git && scmResourceGroup == merge", "group": "inline" }, + { + "command": "git.revealInExplorer", + "when": "scmProvider == git && scmResourceGroup == merge", + "group": "2_view" + }, { "command": "git.openFile2", "when": "scmProvider == git && scmResourceGroup == merge && config.git.showInlineOpenFileAction && config.git.openDiffOnClick", @@ -948,6 +962,11 @@ "when": "scmProvider == git && scmResourceGroup == index", "group": "inline" }, + { + "command": "git.revealInExplorer", + "when": "scmProvider == git && scmResourceGroup == index", + "group": "2_view" + }, { "command": "git.openFile2", "when": "scmProvider == git && scmResourceGroup == index && config.git.showInlineOpenFileAction && config.git.openDiffOnClick", @@ -1007,6 +1026,11 @@ "command": "git.ignore", "when": "scmProvider == git && scmResourceGroup == workingTree", "group": "1_modification@3" + }, + { + "command": "git.revealInExplorer", + "when": "scmProvider == git && scmResourceGroup == workingTree", + "group": "2_view" } ], "editor/title": [ diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 6587b65421d..c6a474439f5 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -56,6 +56,7 @@ "command.publish": "Publish Branch", "command.showOutput": "Show Git Output", "command.ignore": "Add to .gitignore", + "command.revealInExplorer": "Reveal in Explorer", "command.stashIncludeUntracked": "Stash (Include Untracked)", "command.stash": "Stash", "command.stashPop": "Pop Stash...", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts old mode 100755 new mode 100644 index d17c27e7c28..349435d7aeb --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1249,11 +1249,13 @@ export class CommandCenter { promptToSaveFilesBeforeCommit = 'never'; } + const enableSmartCommit = config.get('enableSmartCommit') === true; + if (promptToSaveFilesBeforeCommit !== 'never') { let documents = workspace.textDocuments .filter(d => !d.isUntitled && d.isDirty && isDescendant(repository.root, d.uri.fsPath)); - if (promptToSaveFilesBeforeCommit === 'staged') { + if (promptToSaveFilesBeforeCommit === 'staged' || repository.indexGroup.resourceStates.length > 0) { documents = documents .filter(d => repository.indexGroup.resourceStates.some(s => s.resourceUri.path === d.uri.fsPath)); } @@ -1275,7 +1277,6 @@ export class CommandCenter { } } - const enableSmartCommit = config.get('enableSmartCommit') === true; const enableCommitSigning = config.get('enableCommitSigning') === true; const noStagedChanges = repository.indexGroup.resourceStates.length === 0; const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0; @@ -1370,9 +1371,18 @@ export class CommandCenter { value = (await repository.getCommit(repository.HEAD.commit)).message; } + const branchName = repository.headShortName; + let placeHolder: string; + + if (branchName) { + placeHolder = localize('commitMessageWithHeadLabel2', "Message (commit on '{0}')", branchName); + } else { + placeHolder = localize('commit message', "Commit message"); + } + return await window.showInputBox({ value, - placeHolder: localize('commit message', "Commit message"), + placeHolder, prompt: localize('provide commit message', "Please provide a commit message"), ignoreFocusOut: true }); @@ -2069,6 +2079,19 @@ export class CommandCenter { await this.runByRepository(resources, async (repository, resources) => repository.ignore(resources)); } + @command('git.revealInExplorer') + async revealInExplorer(resourceState: SourceControlResourceState): Promise { + if (!resourceState) { + return; + } + + if (!(resourceState.resourceUri instanceof Uri)) { + return; + } + + await commands.executeCommand('revealInExplorer', resourceState.resourceUri); + } + private async _stash(repository: Repository, includeUntracked = false): Promise { const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0; const noStagedChanges = repository.indexGroup.resourceStates.length === 0; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index fadd1ac6e74..d37dc945f55 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -579,6 +579,23 @@ export class Repository implements Disposable { return this._refs; } + get headShortName(): string | undefined { + if (!this.HEAD) { + return; + } + + const HEAD = this.HEAD; + const tag = this.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0]; + const tagName = tag && tag.name; + const branchName = HEAD.name || tagName || HEAD.commit; + + if (branchName === undefined) { + return; + } + + return branchName.substr(0, 8); + } + private _remotes: Remote[] = []; get remotes(): Remote[] { return this._remotes; @@ -674,7 +691,9 @@ export class Repository implements Disposable { this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, onDotGitFileChange, outputChannel)); const root = Uri.file(repository.root); - this._sourceControl = scm.createSourceControl('git', 'Git', root); + this._sourceControl = scm.createSourceControl('git', 'Git', root, { + treeRendering: true + }); this._sourceControl.acceptInputCommand = { command: 'git.commit', title: localize('commit', "Commit"), arguments: [this._sourceControl] }; this._sourceControl.quickDiffProvider = this; @@ -1457,9 +1476,9 @@ export class Repository implements Disposable { const [refs, remotes, submodules, rebaseCommit] = await Promise.all([this.repository.getRefs({ sort }), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit()]); this._HEAD = HEAD; - this._refs = refs; - this._remotes = remotes; - this._submodules = submodules; + this._refs = refs!; + this._remotes = remotes!; + this._submodules = submodules!; this.rebaseCommit = rebaseCommit; const index: Resource[] = []; @@ -1643,15 +1662,11 @@ export class Repository implements Disposable { } private updateInputBoxPlaceholder(): void { - const HEAD = this.HEAD; - - if (HEAD) { - const tag = this.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0]; - const tagName = tag && tag.name; - const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8); + const branchName = this.headShortName; + if (branchName) { // '{0}' will be replaced by the corresponding key-command later in the process, which is why it needs to stay. - this._sourceControl.inputBox.placeholder = localize('commitMessageWithHeadLabel', "Message ({0} to commit on '{1}')", "{0}", head); + this._sourceControl.inputBox.placeholder = localize('commitMessageWithHeadLabel', "Message ({0} to commit on '{1}')", "{0}", branchName); } else { this._sourceControl.inputBox.placeholder = localize('commitMessage', "Message ({0} to commit)"); } diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index d2e201ca01d..0722cb16fbd 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -160,7 +160,7 @@ export async function mkdirp(path: string, mode?: number): Promise { if (err.code === 'EEXIST') { const stat = await nfcall(fs.stat, path); - if (stat.isDirectory) { + if (stat.isDirectory()) { return; } diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 557fe83ca86..6d6781b9d50 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -197,7 +197,7 @@ }, "dependencies": { "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^5.3.0-next.6", + "vscode-languageclient": "^6.0.0-next.1", "vscode-nls": "^4.1.1" }, "devDependencies": { diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index e6eca4275bf..c11d21bbbd9 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,19 +9,19 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.8", - "vscode-html-languageservice": "^3.0.4-next.3", - "vscode-languageserver": "^5.3.0-next.8", - "vscode-languageserver-types": "3.15.0-next.2", + "vscode-css-languageservice": "^4.0.3-next.11", + "vscode-html-languageservice": "^3.0.4-next.6", + "vscode-languageserver": "^6.0.0-next.1", + "vscode-languageserver-types": "3.15.0-next.5", "vscode-nls": "^4.1.1", "vscode-uri": "^2.0.3" }, "devDependencies": { "@types/mocha": "2.2.33", "@types/node": "^10.14.8", - "glob": "^7.1.2", - "mocha": "^5.2.0", - "mocha-junit-reporter": "^1.17.0", + "glob": "^7.1.4", + "mocha": "^6.1.4", + "mocha-junit-reporter": "^1.23.1", "mocha-multi-reporters": "^1.1.7" }, "scripts": { diff --git a/extensions/html-language-features/server/src/modes/embeddedSupport.ts b/extensions/html-language-features/server/src/modes/embeddedSupport.ts index de80fa5c8e3..df47be3ed97 100644 --- a/extensions/html-language-features/server/src/modes/embeddedSupport.ts +++ b/extensions/html-language-features/server/src/modes/embeddedSupport.ts @@ -56,7 +56,7 @@ export function getDocumentRegions(languageService: LanguageService, document: T } importedScripts.push(value); } else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') { - if (/["'](module|(text|application)\/(java|ecma)script)["']/.test(scanner.getTokenText())) { + if (/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(scanner.getTokenText())) { languageIdFromType = 'javascript'; } else { languageIdFromType = undefined; @@ -228,4 +228,4 @@ function getAttributeLanguage(attributeName: string): string | null { return null; } return match[1] ? 'css' : 'javascript'; -} \ No newline at end of file +} diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 7b9957f070d..27c6f14811c 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -12,11 +12,35 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -35,15 +59,45 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= concat-map@0.0.1: version "0.0.1" @@ -55,12 +109,12 @@ crypt@~0.0.1: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -debug@3.1.0, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: - ms "2.0.0" + ms "^2.1.1" debug@^2.2.0: version "2.6.9" @@ -69,25 +123,115 @@ debug@^2.2.0: dependencies: ms "2.0.0" +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -escape-string-regexp@1.0.5: +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +es-abstract@^1.5.1: + version "1.14.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" + integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.0" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.0.0" + string.prototype.trimright "^2.0.0" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -glob@7.1.2, glob@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -106,10 +250,22 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== inflight@^1.0.4: version "1.0.6" @@ -129,11 +285,78 @@ is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-buffer@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash@^4.16.4: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== +lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + md5@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" @@ -162,10 +385,10 @@ mkdirp@0.5.1, mkdirp@~0.5.1: dependencies: minimist "0.0.8" -mocha-junit-reporter@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz#2e5149ed40fc5d2e3ca71e42db5ab1fec9c6d85c" - integrity sha1-LlFJ7UD8XS48px5C21qx/snG2Fw= +mocha-junit-reporter@^1.23.1: + version "1.23.1" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.1.tgz#ba11519c0b967f404e4123dd69bc4ba022ab0f12" + integrity sha512-qeDvKlZyAH2YJE1vhryvjUQ06t2hcnwwu4k5Ddwn0GQINhgEYFhlGM0DwYCVUHq5cuo32qAW6HDsTHt7zz99Ng== dependencies: debug "^2.2.0" md5 "^2.1.0" @@ -181,28 +404,86 @@ mocha-multi-reporters@^1.1.7: debug "^3.1.0" lodash "^4.16.4" -mocha@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== +mocha@^6.1.4: + version "6.2.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.1.tgz#da941c99437da9bac412097859ff99543969f94c" + integrity sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A== dependencies: + ansi-colors "3.2.3" browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" + debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" - glob "7.1.2" + find-up "3.0.0" + glob "7.1.3" growl "1.10.5" - he "1.1.1" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" minimatch "3.0.4" mkdirp "0.5.1" - supports-color "5.4.0" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.0" + yargs-parser "13.1.1" + yargs-unparser "1.6.0" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +object-inspect@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -210,11 +491,93 @@ once@^1.3.0: dependencies: wrappy "1" +p-limit@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" + integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string.prototype.trimleft@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -222,57 +585,75 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^4.0.3-next.8: - version "4.0.3-next.8" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.8.tgz#0b81693b6ea9d10f78775a1dcad2c0f464fbde16" - integrity sha512-agBPPu86bPKIK5v6CFnWeBXN4jvnCzc67GZa/pvrIWeRdG7nvTu5Y2wYdwdesdpWzno9/5tfFEPp0KJbKQ4l+A== +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: - vscode-languageserver-types "^3.15.0-next.2" + has-flag "^3.0.0" + +vscode-css-languageservice@^4.0.3-next.11: + version "4.0.3-next.11" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.11.tgz#b353ae30551c052e7133f6640d5f4d4755c27fb0" + integrity sha512-6rdruqzWkAbGkTw56WTMS7SmhOYIZ4Dnrs3Wc64cn/saF1f+ib76EUvISiYLZnc+S9FsZu2lnlzPeeJ3LItm2A== + dependencies: + vscode-languageserver-types "^3.15.0-next.5" vscode-nls "^4.1.1" vscode-uri "^2.0.3" -vscode-html-languageservice@^3.0.4-next.3: - version "3.0.4-next.3" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.3.tgz#7a0fc33aae846165b157acbb8b133cc3fcf2ca0d" - integrity sha512-PGIcKFxqsvVMv51QWreuQx9LhN43Vzhgl8RYI8CcWThjl+J8uUKImjwAWq9zndOiiRUPF2Zk7zME/dMIis1hOw== +vscode-html-languageservice@^3.0.4-next.6: + version "3.0.4-next.6" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.6.tgz#ef0f535828f086bcf9bafb2609d54bb285d29d2a" + integrity sha512-DvFpvPJ9wGKIpNa6kxoUSRjZTzLadyKPO2rNhmU7oor9pAQbaNIdJBGoGCm3YELLLPFNcR0/jWDDC4z8wFPk3Q== dependencies: - vscode-languageserver-types "^3.15.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" vscode-nls "^4.1.1" vscode-uri "^2.0.3" -vscode-jsonrpc@^4.1.0-next.2: - version "4.1.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" - integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== +vscode-jsonrpc@^5.0.0-next.2: + version "5.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" + integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== -vscode-languageserver-protocol@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" - integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== +vscode-languageserver-protocol@^3.15.0-next.9: + version "3.15.0-next.9" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz#e768256bd5b580b25bfbc8099bc03bc4c42ebf30" + integrity sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g== dependencies: - vscode-jsonrpc "^4.1.0-next.2" - vscode-languageserver-types "^3.15.0-next.2" + vscode-jsonrpc "^5.0.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" -vscode-languageserver-types@3.15.0-next.2, vscode-languageserver-types@^3.15.0-next.2: - version "3.15.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" - integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== +vscode-languageserver-types@3.15.0-next.5, vscode-languageserver-types@^3.15.0-next.5: + version "3.15.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b" + integrity sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw== -vscode-languageserver@^5.3.0-next.8: - version "5.3.0-next.8" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.8.tgz#12a4adf60374dbb93e153e08bdca5525f9b2029f" - integrity sha512-6vUb96wsRfrFqndril3gct/FBCSc24OxFZ2iz7kuEuXvLaIcEVOcSZIqQK8oFN7PdbAIaa9nnIpKSy4Yd15cIw== +vscode-languageserver@^6.0.0-next.1: + version "6.0.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.0.0-next.1.tgz#4d71886d4a17d22eafc61b3a5fbf84e8e27c191f" + integrity sha512-LSF6bXoFeXfMPRNyqzI3yFX/kD2DzXBemqvyj1kDWNVraiWttm4xKF4YXsvJ7Z3s9sVt/Dpu3CFU3w61PGNZMg== dependencies: - vscode-languageserver-protocol "^3.15.0-next.6" + vscode-languageserver-protocol "^3.15.0-next.9" vscode-textbuffer "^1.0.0" - vscode-uri "^1.0.6" vscode-nls@^4.1.1: version "4.1.1" @@ -284,16 +665,39 @@ vscode-textbuffer@^1.0.0: resolved "https://registry.yarnpkg.com/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz#1faee638c8e0e4131c8d5c353993a1874acda086" integrity sha512-zPaHo4urgpwsm+PrJWfNakolRpryNja18SUip/qIIsfhuEqEIPEXMxHOlFPjvDC4JgTaimkncNW7UMXRJTY6ow== -vscode-uri@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" - integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== - vscode-uri@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543" integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw== +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -303,3 +707,41 @@ xml@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@13.1.1, yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.0, yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 21d801f04a0..195143d17dd 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -28,11 +28,16 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" -semver@^5.3.0, semver@^5.5.0: +semver@^5.3.0: version "5.5.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + vscode-extension-telemetry@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" @@ -40,31 +45,31 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^4.1.0-next.2: - version "4.1.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" - integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== +vscode-jsonrpc@^5.0.0-next.2: + version "5.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" + integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== -vscode-languageclient@^5.3.0-next.6: - version "5.3.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.3.0-next.6.tgz#35e74882781158e8b111911c0953869d3df08777" - integrity sha512-DxT8+gkenjCjJV6ArcP75/AQfx6HP6m6kHIbacPCpffMeoE1YMLKj6ZixA9J87yr0fMtBmqumLmDeGe7MIF2bw== +vscode-languageclient@^6.0.0-next.1: + version "6.0.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.0.0-next.1.tgz#deca1743afd20da092e04e40ef73cedbbd978455" + integrity sha512-eJ9VjLFNINArgRzLbQ11YlWry7dM93GEODkQBXTRfrSypksiO9qSGr4SHhWgxxP26p4FRSpzc/17+N+Egnnchg== dependencies: - semver "^5.5.0" - vscode-languageserver-protocol "^3.15.0-next.6" + semver "^6.3.0" + vscode-languageserver-protocol "^3.15.0-next.9" -vscode-languageserver-protocol@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" - integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== +vscode-languageserver-protocol@^3.15.0-next.9: + version "3.15.0-next.9" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz#e768256bd5b580b25bfbc8099bc03bc4c42ebf30" + integrity sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g== dependencies: - vscode-jsonrpc "^4.1.0-next.2" - vscode-languageserver-types "^3.15.0-next.2" + vscode-jsonrpc "^5.0.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" -vscode-languageserver-types@^3.15.0-next.2: - version "3.15.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" - integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== +vscode-languageserver-types@^3.15.0-next.5: + version "3.15.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b" + integrity sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw== vscode-nls@^4.1.1: version "4.1.1" diff --git a/extensions/image-preview/README.md b/extensions/image-preview/README.md index e8664c77a90..ccb5ac3954a 100644 --- a/extensions/image-preview/README.md +++ b/extensions/image-preview/README.md @@ -1,3 +1,17 @@ # Image Preview **Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. + +## Features + +This extension provides VS Code's built-in image preview functionality. + +Supported image formats: + +- `*.jpg`, `*.jpe`, `*.jpeg` +- `*.png` +- `*.bmp` +- `*.gif` +- `*.ico` +- `*.tga` +- `*.webp` diff --git a/extensions/image-preview/media/main.css b/extensions/image-preview/media/main.css index 971a641fb07..0f50cef683c 100644 --- a/extensions/image-preview/media/main.css +++ b/extensions/image-preview/media/main.css @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ html, body { + width: 100%; height: 100%; - max-height: 100%; + text-align: center; } body img { @@ -77,22 +78,38 @@ body img { margin-left: 5px; } -.loading { - position: fixed; +.container.loading, +.container.error { + display: flex; + justify-content: center; + align-items: center; +} + +.loading-indicator { width: 30px; height: 30px; - left: 50%; - top: 50%; - margin-top: -15px; - margin-left: -15px; background-image: url('./loading.svg'); background-size: cover; } -.vscode-dark .loading { +.loading-indicator, +.image-load-error-message { + display: none; +} + +.loading .loading-indicator, +.error .image-load-error-message { + display: block; +} + +.image-load-error-message { + margin: 1em; +} + +.vscode-dark .loading-indicator { background-image: url('./loading-dark.svg'); } -.vscode-high-contrast .loading { +.vscode-high-contrast .loading-indicator { background-image: url('./loading-hc.svg'); } diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index e58b7e58f36..34c9708df99 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -72,7 +72,7 @@ let hasLoadedImage = false; // Elements - const container = /** @type {HTMLElement} */(document.querySelector('body')); + const container = document.body; const image = document.createElement('img'); function updateScale(newScale) { @@ -88,9 +88,6 @@ image.style.width = 'auto'; vscode.setState(undefined); } else { - const oldWidth = image.width; - const oldHeight = image.height; - scale = clamp(newScale, MIN_SCALE, MAX_SCALE); if (scale >= PIXELATION_THRESHOLD) { image.classList.add('pixelated'); @@ -98,25 +95,19 @@ image.classList.remove('pixelated'); } - const { scrollTop, scrollLeft } = image.parentElement; - const dx = (scrollLeft + image.parentElement.clientWidth / 2) / image.parentElement.scrollWidth; - const dy = (scrollTop + image.parentElement.clientHeight / 2) / image.parentElement.scrollHeight; + const dx = (window.scrollX + container.clientWidth / 2) / container.scrollWidth; + const dy = (window.scrollY + container.clientHeight / 2) / container.scrollHeight; image.classList.remove('scale-to-fit'); image.style.minWidth = `${(image.naturalWidth * scale)}px`; image.style.width = `${(image.naturalWidth * scale)}px`; - const newWidth = image.width; - const scaleFactor = (newWidth - oldWidth) / oldWidth; + const newScrollX = container.scrollWidth * dx - container.clientWidth / 2; + const newScrollY = container.scrollHeight * dy - container.clientHeight / 2; - const newScrollLeft = ((oldWidth * scaleFactor * dx) + scrollLeft); - const newScrollTop = ((oldHeight * scaleFactor * dy) + scrollTop); - // scrollbar.setScrollPosition({ - // scrollLeft: newScrollLeft, - // scrollTop: newScrollTop, - // }); + window.scrollTo(newScrollX, newScrollY); - vscode.setState({ scale: scale, offsetX: newScrollLeft, offsetY: newScrollTop }); + vscode.setState({ scale: scale, offsetX: newScrollX, offsetY: newScrollY }); } vscode.postMessage({ @@ -232,19 +223,15 @@ image.classList.add('scale-to-fit'); image.addEventListener('load', () => { - document.querySelector('.loading').remove(); hasLoadedImage = true; - if (!image) { - return; - } - vscode.postMessage({ type: 'size', value: `${image.naturalWidth}x${image.naturalHeight}`, }); - container.classList.add('ready'); + document.body.classList.remove('loading'); + document.body.classList.add('ready'); document.body.append(image); updateScale(scale); @@ -254,6 +241,12 @@ } }); + image.addEventListener('error', () => { + hasLoadedImage = true; + document.body.classList.add('error'); + document.body.classList.remove('loading'); + }); + image.src = decodeURI(settings.src); window.addEventListener('message', e => { diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index 7db7b2fb17e..7dd872ae1bc 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -26,7 +26,7 @@ "displayName": "%webviewEditors.displayName%", "selector": [ { - "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,tga,tif,tiff,webp}", + "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,tga,webp}", "mime": "image/*" } ] diff --git a/extensions/image-preview/package.nls.json b/extensions/image-preview/package.nls.json index 78c753d1d54..44359cba8de 100644 --- a/extensions/image-preview/package.nls.json +++ b/extensions/image-preview/package.nls.json @@ -1,5 +1,5 @@ { "displayName": "Image Preview", - "description": "Previews images.", + "description": "Provides VS Code's built-in image preview", "webviewEditors.displayName": "Image Preview" } diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index b0f6ff4c357..2d5963adc1b 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -4,19 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { SizeStatusBarEntry } from './sizeStatusBarEntry'; -import { ZoomStatusBarEntry } from './zoomStatusBarEntry'; +import * as nls from 'vscode-nls'; import { Disposable } from './dispose'; +import { SizeStatusBarEntry } from './sizeStatusBarEntry'; +import { Scale, ZoomStatusBarEntry } from './zoomStatusBarEntry'; + +const localize = nls.loadMessageBundle(); + +const enum PreviewState { + Disposed, + Visible, + Active, +} export class Preview extends Disposable { public static readonly viewType = 'imagePreview.previewEditor'; - private _active = true; + private readonly id: string = `${Date.now()}-${Math.random().toString()}`; + + private _previewState = PreviewState.Visible; + private _imageSize: string | undefined; + private _imageZoom: Scale | undefined; constructor( private readonly extensionRoot: vscode.Uri, - resource: vscode.Uri, + private readonly resource: vscode.Uri, private readonly webviewEditor: vscode.WebviewEditor, private readonly sizeStatusBarEntry: SizeStatusBarEntry, private readonly zoomStatusBarEntry: ZoomStatusBarEntry, @@ -34,56 +47,90 @@ export class Preview extends Disposable { ] }; - webviewEditor.webview.html = this.getWebiewContents(webviewEditor, resource); - this._register(webviewEditor.webview.onDidReceiveMessage(message => { switch (message.type) { case 'size': { - this.sizeStatusBarEntry.update(message.value); + this._imageSize = message.value; + this.update(); break; } case 'zoom': { - this.zoomStatusBarEntry.update(message.value); + this._imageZoom = message.value; + this.update(); break; } } })); this._register(zoomStatusBarEntry.onDidChangeScale(e => { - this.webviewEditor.webview.postMessage({ type: 'setScale', scale: e.scale }); + if (this._previewState === PreviewState.Active) { + this.webviewEditor.webview.postMessage({ type: 'setScale', scale: e.scale }); + } })); this._register(webviewEditor.onDidChangeViewState(() => { this.update(); })); + this._register(webviewEditor.onDidDispose(() => { - if (this._active) { - this.sizeStatusBarEntry.hide(); - this.zoomStatusBarEntry.hide(); + if (this._previewState === PreviewState.Active) { + this.sizeStatusBarEntry.hide(this.id); + this.zoomStatusBarEntry.hide(this.id); + } + this._previewState = PreviewState.Disposed; + })); + + const watcher = this._register(vscode.workspace.createFileSystemWatcher(resource.fsPath)); + this._register(watcher.onDidChange(e => { + if (e.toString() === this.resource.toString()) { + this.render(); } })); + this._register(watcher.onDidDelete(e => { + if (e.toString() === this.resource.toString()) { + this.webviewEditor.dispose(); + } + })); + + this.render(); this.update(); } - private update() { - this._active = this.webviewEditor.active; - if (this._active) { - this.sizeStatusBarEntry.show(); - this.zoomStatusBarEntry.show(); - } else { - this.sizeStatusBarEntry.hide(); - this.zoomStatusBarEntry.hide(); + private render() { + if (this._previewState !== PreviewState.Disposed) { + this.webviewEditor.webview.html = this.getWebiewContents(); } } - private getWebiewContents(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri): string { + private update() { + if (this._previewState === PreviewState.Disposed) { + return; + } + + if (this.webviewEditor.active) { + this._previewState = PreviewState.Active; + this.sizeStatusBarEntry.show(this.id, this._imageSize || ''); + this.zoomStatusBarEntry.show(this.id, this._imageZoom || 'fit'); + } else { + if (this._previewState === PreviewState.Active) { + this.sizeStatusBarEntry.hide(this.id); + this.zoomStatusBarEntry.hide(this.id); + } + this._previewState = PreviewState.Visible; + } + } + + private getWebiewContents(): string { + const version = Date.now().toString(); const settings = { isMac: process.platform === 'darwin', - src: this.getResourcePath(webviewEditor, resource) + src: this.getResourcePath(this.webviewEditor, this.resource, version), }; + const nonce = Date.now().toString(); + return /* html */` @@ -91,23 +138,33 @@ export class Preview extends Disposable { Image Preview - + + + - -
- + +
+
${localize('preview.imageLoadError', "An error occurred while loading the image")}
+ `; } - private getResourcePath(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri) { - if (resource.scheme === 'data') { - return encodeURI(resource.toString(true)); - } + private getResourcePath(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri, version: string) { + switch (resource.scheme) { + case 'data': + return encodeURI(resource.toString(true)); - return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true)); + case 'git': + // Show blank image + return encodeURI('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg=='); + + + default: + return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true) + `?version=${version}`); + } } private extensionResource(path: string) { diff --git a/extensions/image-preview/src/sizeStatusBarEntry.ts b/extensions/image-preview/src/sizeStatusBarEntry.ts index 88f75f0cfd6..6b2bff746cf 100644 --- a/extensions/image-preview/src/sizeStatusBarEntry.ts +++ b/extensions/image-preview/src/sizeStatusBarEntry.ts @@ -5,29 +5,35 @@ import * as vscode from 'vscode'; import { Disposable } from './dispose'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); export class SizeStatusBarEntry extends Disposable { private readonly _entry: vscode.StatusBarItem; + private _showingOwner: string | undefined; + constructor() { super(); this._entry = this._register(vscode.window.createStatusBarItem({ id: 'imagePreview.size', - name: 'Image Size', + name: localize('sizeStatusBar.name', "Image Size"), alignment: vscode.StatusBarAlignment.Right, priority: 101 /* to the left of editor status (100) */, })); } - public show() { + public show(owner: string, text: string) { + this._showingOwner = owner; + this._entry.text = text; this._entry.show(); } - public hide() { - this._entry.hide(); + public hide(owner: string) { + if (owner === this._showingOwner) { + this._entry.hide(); + this._showingOwner = undefined; + } } - - public update(text: string) { - this._entry.text = text; - } -} \ No newline at end of file +} diff --git a/extensions/image-preview/src/zoomStatusBarEntry.ts b/extensions/image-preview/src/zoomStatusBarEntry.ts index 11580b84299..dc102a48d64 100644 --- a/extensions/image-preview/src/zoomStatusBarEntry.ts +++ b/extensions/image-preview/src/zoomStatusBarEntry.ts @@ -11,7 +11,7 @@ const localize = nls.loadMessageBundle(); const selectZoomLevelCommandId = '_imagePreview.selectZoomLevel'; -type Scale = number | 'fit'; +export type Scale = number | 'fit'; export class ZoomStatusBarEntry extends Disposable { private readonly _entry: vscode.StatusBarItem; @@ -19,11 +19,13 @@ export class ZoomStatusBarEntry extends Disposable { private readonly _onDidChangeScale = this._register(new vscode.EventEmitter<{ scale: Scale }>()); public readonly onDidChangeScale = this._onDidChangeScale.event; + private _showOwner: string | undefined; + constructor() { super(); this._entry = this._register(vscode.window.createStatusBarItem({ id: 'imagePreview.zoom', - name: 'Image Zoom', + name: localize('zoomStatusBar.name', "Image Zoom"), alignment: vscode.StatusBarAlignment.Right, priority: 102 /* to the left of editor size entry (101) */, })); @@ -48,16 +50,17 @@ export class ZoomStatusBarEntry extends Disposable { this._entry.command = selectZoomLevelCommandId; } - public show() { + public show(owner: string, scale: Scale) { + this._showOwner = owner; + this._entry.text = this.zoomLabel(scale); this._entry.show(); } - public hide() { - this._entry.hide(); - } - - public update(scale: Scale) { - this._entry.text = this.zoomLabel(scale); + public hide(owner: string) { + if (owner === this._showOwner) { + this._entry.hide(); + this._showOwner = undefined; + } } private zoomLabel(scale: Scale): string { @@ -65,4 +68,4 @@ export class ZoomStatusBarEntry extends Disposable { ? localize('zoomStatusBar.wholeImageLabel', "Whole Image") : `${Math.round(scale * 100)}%`; } -} \ No newline at end of file +} diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json index 6909de1400f..8d315c45835 100644 --- a/extensions/javascript/package.json +++ b/extensions/javascript/package.json @@ -98,36 +98,6 @@ "language": "javascriptreact", "path": "./snippets/javascript.json" } - ], - "jsonValidation": [ - { - "fileMatch": ".bowerrc", - "url": "https://schemastore.azurewebsites.net/schemas/json/bowerrc.json" - }, - { - "fileMatch": ".babelrc", - "url": "https://schemastore.azurewebsites.net/schemas/json/babelrc.json" - }, - { - "fileMatch": ".babelrc.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/babelrc.json" - }, - { - "fileMatch": "jsconfig.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" - }, - { - "fileMatch": "jsconfig.json", - "url": "./schemas/jsconfig.schema.json" - }, - { - "fileMatch": "jsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" - }, - { - "fileMatch": "jsconfig.*.json", - "url": "./schemas/jsconfig.schema.json" - } ] } -} \ No newline at end of file +} diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 167583302c2..cc61c9163c5 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -104,7 +104,7 @@ "dependencies": { "request-light": "^0.2.4", "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^5.3.0-next.6", + "vscode-languageclient": "^6.0.0-next.1", "vscode-nls": "^4.1.1" }, "devDependencies": { diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index dbc72d14601..71356e3c441 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -44,7 +44,7 @@ The JSON language server has the following dependencies on the client's capabili The client can send the following initialization options to the server: -- `provideFormatter: boolean | undefined`. If defined, the value defines wheter the server provides the `documentRangeFormattingProvider` capability on initialization. If undefined, the setting `json.format.enable` is used to determined wheter formatting is provided. The formatter will then be registered through dynamic registration. If the client does not support dynamic registration, no formatter will be available. +- `provideFormatter: boolean | undefined`. If defined, the value defines whether the server provides the `documentRangeFormattingProvider` capability on initialization. If undefined, the setting `json.format.enable` is used to determine whether formatting is provided. The formatter will then be registered through dynamic registration. If the client does not support dynamic registration, no formatter will be available. - `handledSchemaProtocols`: The URI schemas handles by the server. See section `Schema configuration` below. ### Settings @@ -60,7 +60,7 @@ The server supports the following settings: - `format` - `enable`: Whether the server should register the formatting support. This option is only applicable if the client supports *dynamicRegistration* for *rangeFormatting* and `initializationOptions.provideFormatter` is not defined. - `schema`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. - - `fileMatch`: an array or file names or paths (separated by `/`). `*` can be used as a wildcard. + - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. - `url`: The URL of the schema, optional when also a schema is provided. - `schema`: The schema content. @@ -99,9 +99,9 @@ To find the schema for a given JSON document, the server uses the following mech - The settings define a schema association based on the documents URL. Settings can either associate a schema URL to a file or path pattern, and they can directly provide a schema. - Additionally, schema associations can also be provided by a custom 'schemaAssociations' configuration call. -Schemas are identified by URLs. To load the content of a schema, the JSON language server either tries to load from that URI or path itself, or delegates to the client. +Schemas are identified by URLs. To load the content of a schema, the JSON language server either tries to load from that URI or path itself or delegates to the client. -The `initializationOptions.handledSchemaProtocols` initialization option defines which URLs are handled by the server. Requests for all other URIs are send to the client. +The `initializationOptions.handledSchemaProtocols` initialization option defines which URLs are handled by the server. Requests for all other URIs are sent to the client. `handledSchemaProtocols` is part of the initialization options and can't be changed while the server is running. @@ -121,7 +121,7 @@ If `handledSchemaProtocols` is not set, the JSON language server will load the f #### Schema content request -Requests for schemas with URLs not handled by the server are forwarded to the client through an LSP request. This request is a JSON language server specific, non-standardized, extension to the LSP. +Requests for schemas with URLs not handled by the server are forwarded to the client through an LSP request. This request is a JSON language server-specific, non-standardized, extension to the LSP. Request: - method: 'vscode/content' @@ -130,12 +130,12 @@ Request: #### Schema content change notification -When the client is aware that a schema content has changed, it will notify the server through a notification. This notification is a JSON language server specific, non-standardized, extension to the LSP. +When the client is aware that a schema content has changed, it will notify the server through a notification. This notification is a JSON language server-specific, non-standardized, extension to the LSP. The server will, as a response, clear the schema content from the cache and reload the schema content when required again. #### Schema associations notification -In addition to the settings, schemas associations can also be provided through a notification from the client to the server. This notification is a JSON language server specific, non-standardized, extension to the LSP. +In addition to the settings, schemas associations can also be provided through a notification from the client to the server. This notification is a JSON language server-specific, non-standardized, extension to the LSP. Notification: - method: 'json/schemaAssociations' diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 28c46483250..f3ddc4293bb 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-json-languageserver", "description": "JSON language server", - "version": "1.2.1", + "version": "1.2.2", "author": "Microsoft Corporation", "license": "MIT", "engines": { @@ -14,8 +14,8 @@ "dependencies": { "jsonc-parser": "^2.1.1", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.3.4", - "vscode-languageserver": "^5.3.0-next.8", + "vscode-json-languageservice": "^3.3.5", + "vscode-languageserver": "^6.0.0-next.1", "vscode-nls": "^4.1.1", "vscode-uri": "^2.0.3" }, diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index 74561395ce8..2439da72e0d 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -144,7 +144,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (params.initializationOptions.provideFormatter === undefined); + dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions.provideFormatter !== 'boolean'); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); const capabilities: ServerCapabilities = { @@ -153,11 +153,10 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : undefined, hoverProvider: true, documentSymbolProvider: true, - documentRangeFormattingProvider: false, + documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true, colorProvider: {}, foldingRangeProvider: true, - selectionRangeProvider: true, - documentFormattingProvider: params.initializationOptions.provideFormatter === true + selectionRangeProvider: true }; return { capabilities }; diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 8230b9546bc..0ceb7ddcb1e 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -73,42 +73,41 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.4.tgz#4ff67580491d3a5dc469f4a78643f20adff0278d" - integrity sha512-/nuI4uDBfxyVyeGtBdYwP/tIaXYKOoymUOSozYKLzsmrDmu555gZpzc11LrARa96z92wSaa5hfjTtNMAoM2mxw== +vscode-json-languageservice@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.5.tgz#e222e8391beeb23cfa40cf17fd57d1594d295fc7" + integrity sha512-Le6SG5aRdrRc5jVeVMRkYbGH9rrVaZHCW0Oa8zCFQ0T8viUud9qdZ29lSv5NPNLwTB8mn4pYucFyyEPM2YWvLA== dependencies: jsonc-parser "^2.1.1" - vscode-languageserver-types "^3.15.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" vscode-nls "^4.1.1" vscode-uri "^2.0.3" -vscode-jsonrpc@^4.1.0-next.2: - version "4.1.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" - integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== +vscode-jsonrpc@^5.0.0-next.2: + version "5.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" + integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== -vscode-languageserver-protocol@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" - integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== +vscode-languageserver-protocol@^3.15.0-next.9: + version "3.15.0-next.9" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz#e768256bd5b580b25bfbc8099bc03bc4c42ebf30" + integrity sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g== dependencies: - vscode-jsonrpc "^4.1.0-next.2" - vscode-languageserver-types "^3.15.0-next.2" + vscode-jsonrpc "^5.0.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" -vscode-languageserver-types@^3.15.0-next.2: - version "3.15.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" - integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== +vscode-languageserver-types@^3.15.0-next.5: + version "3.15.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b" + integrity sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw== -vscode-languageserver@^5.3.0-next.8: - version "5.3.0-next.8" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.8.tgz#12a4adf60374dbb93e153e08bdca5525f9b2029f" - integrity sha512-6vUb96wsRfrFqndril3gct/FBCSc24OxFZ2iz7kuEuXvLaIcEVOcSZIqQK8oFN7PdbAIaa9nnIpKSy4Yd15cIw== +vscode-languageserver@^6.0.0-next.1: + version "6.0.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.0.0-next.1.tgz#4d71886d4a17d22eafc61b3a5fbf84e8e27c191f" + integrity sha512-LSF6bXoFeXfMPRNyqzI3yFX/kD2DzXBemqvyj1kDWNVraiWttm4xKF4YXsvJ7Z3s9sVt/Dpu3CFU3w61PGNZMg== dependencies: - vscode-languageserver-protocol "^3.15.0-next.6" + vscode-languageserver-protocol "^3.15.0-next.9" vscode-textbuffer "^1.0.0" - vscode-uri "^1.0.6" vscode-nls@^4.0.0: version "4.0.0" @@ -125,11 +124,6 @@ vscode-textbuffer@^1.0.0: resolved "https://registry.yarnpkg.com/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz#1faee638c8e0e4131c8d5c353993a1874acda086" integrity sha512-zPaHo4urgpwsm+PrJWfNakolRpryNja18SUip/qIIsfhuEqEIPEXMxHOlFPjvDC4JgTaimkncNW7UMXRJTY6ow== -vscode-uri@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" - integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== - vscode-uri@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index fdc89dd7cf1..b3804735a74 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -101,10 +101,10 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== -semver@^5.5.0: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" - integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== vscode-extension-telemetry@0.1.1: version "0.1.1" @@ -113,31 +113,31 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^4.1.0-next.2: - version "4.1.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" - integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== +vscode-jsonrpc@^5.0.0-next.2: + version "5.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" + integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== -vscode-languageclient@^5.3.0-next.6: - version "5.3.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.3.0-next.6.tgz#35e74882781158e8b111911c0953869d3df08777" - integrity sha512-DxT8+gkenjCjJV6ArcP75/AQfx6HP6m6kHIbacPCpffMeoE1YMLKj6ZixA9J87yr0fMtBmqumLmDeGe7MIF2bw== +vscode-languageclient@^6.0.0-next.1: + version "6.0.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.0.0-next.1.tgz#deca1743afd20da092e04e40ef73cedbbd978455" + integrity sha512-eJ9VjLFNINArgRzLbQ11YlWry7dM93GEODkQBXTRfrSypksiO9qSGr4SHhWgxxP26p4FRSpzc/17+N+Egnnchg== dependencies: - semver "^5.5.0" - vscode-languageserver-protocol "^3.15.0-next.6" + semver "^6.3.0" + vscode-languageserver-protocol "^3.15.0-next.9" -vscode-languageserver-protocol@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" - integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== +vscode-languageserver-protocol@^3.15.0-next.9: + version "3.15.0-next.9" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.9.tgz#e768256bd5b580b25bfbc8099bc03bc4c42ebf30" + integrity sha512-b9PAxouMmtsLEe8ZjbIMPb7wRWPhckGfgjwZLmp/dWnaAuRPYtY3lGO0/rNbLc3jKIqCVlnEyYVFKalzDAzj0g== dependencies: - vscode-jsonrpc "^4.1.0-next.2" - vscode-languageserver-types "^3.15.0-next.2" + vscode-jsonrpc "^5.0.0-next.2" + vscode-languageserver-types "^3.15.0-next.5" -vscode-languageserver-types@^3.15.0-next.2: - version "3.15.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" - integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== +vscode-languageserver-types@^3.15.0-next.5: + version "3.15.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz#863d711bf47b338ff5e63ae19fb20d4fcd4d713b" + integrity sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index 9a73ac64aae..7faa70cef7a 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -12,8 +12,7 @@ { "open": "[", "close": "]", "notIn": ["string"] }, { "open": "(", "close": ")", "notIn": ["string"] }, { "open": "'", "close": "'", "notIn": ["string"] }, - { "open": "/*", "close": "*/", "notIn": ["string"] }, { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, { "open": "`", "close": "`", "notIn": ["string", "comment"] } ] -} \ No newline at end of file +} diff --git a/extensions/json/package.json b/extensions/json/package.json index 6ef4b971691..e66955770bc 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -27,7 +27,8 @@ ".swcrc", ".webmanifest", ".js.map", - ".css.map" + ".css.map", + ".har" ], "filenames": [ "composer.lock", diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 61b975b7015..269140f643b 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/00b05ebe6850083664d92d0eba6e5ee8f153baa6", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/46724e2885f9557400ed91727d75c3574ceded3a", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -1682,6 +1682,39 @@ } ] }, + "fenced_code_block_log": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(log)(\\s+[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "4": { + "name": "fenced_code.block.language.markdown" + }, + "5": { + "name": "fenced_code.block.language.attributes.markdown" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.log", + "patterns": [ + { + "include": "text.log" + } + ] + } + ] + }, "fenced_code_block": { "patterns": [ { @@ -1831,6 +1864,9 @@ { "include": "#fenced_code_block_markdown" }, + { + "include": "#fenced_code_block_log" + }, { "include": "#fenced_code_block_unknown" } diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index d46e19c8c97..666f4394dbb 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -1,1008 +1,2 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./preview-src/index.ts"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/lodash.throttle/index.js": -/*!***********************************************!*\ - !*** ./node_modules/lodash.throttle/index.js ***! - \***********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(global) {/** - * lodash (Custom Build) - * Build: `lodash modularize exports="npm" -o ./` - * Copyright jQuery Foundation and other contributors - * Released under MIT license - * Based on Underscore.js 1.8.3 - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - */ - -/** Used as the `TypeError` message for "Functions" methods. */ -var FUNC_ERROR_TEXT = 'Expected a function'; - -/** Used as references for various `Number` constants. */ -var NAN = 0 / 0; - -/** `Object#toString` result references. */ -var symbolTag = '[object Symbol]'; - -/** Used to match leading and trailing whitespace. */ -var reTrim = /^\s+|\s+$/g; - -/** Used to detect bad signed hexadecimal string values. */ -var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; - -/** Used to detect binary string values. */ -var reIsBinary = /^0b[01]+$/i; - -/** Used to detect octal string values. */ -var reIsOctal = /^0o[0-7]+$/i; - -/** Built-in method references without a dependency on `root`. */ -var freeParseInt = parseInt; - -/** Detect free variable `global` from Node.js. */ -var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; - -/** Detect free variable `self`. */ -var freeSelf = typeof self == 'object' && self && self.Object === Object && self; - -/** Used as a reference to the global object. */ -var root = freeGlobal || freeSelf || Function('return this')(); - -/** Used for built-in method references. */ -var objectProto = Object.prototype; - -/** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ -var objectToString = objectProto.toString; - -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeMax = Math.max, - nativeMin = Math.min; - -/** - * Gets the timestamp of the number of milliseconds that have elapsed since - * the Unix epoch (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Date - * @returns {number} Returns the timestamp. - * @example - * - * _.defer(function(stamp) { - * console.log(_.now() - stamp); - * }, _.now()); - * // => Logs the number of milliseconds it took for the deferred invocation. - */ -var now = function() { - return root.Date.now(); -}; - -/** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked. The debounced function comes with a `cancel` method to cancel - * delayed `func` invocations and a `flush` method to immediately invoke them. - * Provide `options` to indicate whether `func` should be invoked on the - * leading and/or trailing edge of the `wait` timeout. The `func` is invoked - * with the last arguments provided to the debounced function. Subsequent - * calls to the debounced function return the result of the last `func` - * invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the debounced function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.debounce` and `_.throttle`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] The number of milliseconds to delay. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=false] - * Specify invoking on the leading edge of the timeout. - * @param {number} [options.maxWait] - * The maximum time `func` is allowed to be delayed before it's invoked. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // Avoid costly calculations while the window size is in flux. - * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); - * - * // Invoke `sendMail` when clicked, debouncing subsequent calls. - * jQuery(element).on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })); - * - * // Ensure `batchLog` is invoked once after 1 second of debounced calls. - * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); - * var source = new EventSource('/stream'); - * jQuery(source).on('message', debounced); - * - * // Cancel the trailing debounced invocation. - * jQuery(window).on('popstate', debounced.cancel); - */ -function debounce(func, wait, options) { - var lastArgs, - lastThis, - maxWait, - result, - timerId, - lastCallTime, - lastInvokeTime = 0, - leading = false, - maxing = false, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - wait = toNumber(wait) || 0; - if (isObject(options)) { - leading = !!options.leading; - maxing = 'maxWait' in options; - maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - - function invokeFunc(time) { - var args = lastArgs, - thisArg = lastThis; - - lastArgs = lastThis = undefined; - lastInvokeTime = time; - result = func.apply(thisArg, args); - return result; - } - - function leadingEdge(time) { - // Reset any `maxWait` timer. - lastInvokeTime = time; - // Start the timer for the trailing edge. - timerId = setTimeout(timerExpired, wait); - // Invoke the leading edge. - return leading ? invokeFunc(time) : result; - } - - function remainingWait(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime, - result = wait - timeSinceLastCall; - - return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; - } - - function shouldInvoke(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime; - - // Either this is the first call, activity has stopped and we're at the - // trailing edge, the system time has gone backwards and we're treating - // it as the trailing edge, or we've hit the `maxWait` limit. - return (lastCallTime === undefined || (timeSinceLastCall >= wait) || - (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); - } - - function timerExpired() { - var time = now(); - if (shouldInvoke(time)) { - return trailingEdge(time); - } - // Restart the timer. - timerId = setTimeout(timerExpired, remainingWait(time)); - } - - function trailingEdge(time) { - timerId = undefined; - - // Only invoke if we have `lastArgs` which means `func` has been - // debounced at least once. - if (trailing && lastArgs) { - return invokeFunc(time); - } - lastArgs = lastThis = undefined; - return result; - } - - function cancel() { - if (timerId !== undefined) { - clearTimeout(timerId); - } - lastInvokeTime = 0; - lastArgs = lastCallTime = lastThis = timerId = undefined; - } - - function flush() { - return timerId === undefined ? result : trailingEdge(now()); - } - - function debounced() { - var time = now(), - isInvoking = shouldInvoke(time); - - lastArgs = arguments; - lastThis = this; - lastCallTime = time; - - if (isInvoking) { - if (timerId === undefined) { - return leadingEdge(lastCallTime); - } - if (maxing) { - // Handle invocations in a tight loop. - timerId = setTimeout(timerExpired, wait); - return invokeFunc(lastCallTime); - } - } - if (timerId === undefined) { - timerId = setTimeout(timerExpired, wait); - } - return result; - } - debounced.cancel = cancel; - debounced.flush = flush; - return debounced; -} - -/** - * Creates a throttled function that only invokes `func` at most once per - * every `wait` milliseconds. The throttled function comes with a `cancel` - * method to cancel delayed `func` invocations and a `flush` method to - * immediately invoke them. Provide `options` to indicate whether `func` - * should be invoked on the leading and/or trailing edge of the `wait` - * timeout. The `func` is invoked with the last arguments provided to the - * throttled function. Subsequent calls to the throttled function return the - * result of the last `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the throttled function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.throttle` and `_.debounce`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to throttle. - * @param {number} [wait=0] The number of milliseconds to throttle invocations to. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=true] - * Specify invoking on the leading edge of the timeout. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // Avoid excessively updating the position while scrolling. - * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); - * - * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. - * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); - * jQuery(element).on('click', throttled); - * - * // Cancel the trailing throttled invocation. - * jQuery(window).on('popstate', throttled.cancel); - */ -function throttle(func, wait, options) { - var leading = true, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (isObject(options)) { - leading = 'leading' in options ? !!options.leading : leading; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - return debounce(func, wait, { - 'leading': leading, - 'maxWait': wait, - 'trailing': trailing - }); -} - -/** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ -function isObject(value) { - var type = typeof value; - return !!value && (type == 'object' || type == 'function'); -} - -/** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ -function isObjectLike(value) { - return !!value && typeof value == 'object'; -} - -/** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ -function isSymbol(value) { - return typeof value == 'symbol' || - (isObjectLike(value) && objectToString.call(value) == symbolTag); -} - -/** - * Converts `value` to a number. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {number} Returns the number. - * @example - * - * _.toNumber(3.2); - * // => 3.2 - * - * _.toNumber(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toNumber(Infinity); - * // => Infinity - * - * _.toNumber('3.2'); - * // => 3.2 - */ -function toNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol(value)) { - return NAN; - } - if (isObject(value)) { - var other = typeof value.valueOf == 'function' ? value.valueOf() : value; - value = isObject(other) ? (other + '') : other; - } - if (typeof value != 'string') { - return value === 0 ? value : +value; - } - value = value.replace(reTrim, ''); - var isBinary = reIsBinary.test(value); - return (isBinary || reIsOctal.test(value)) - ? freeParseInt(value.slice(2), isBinary ? 2 : 8) - : (reIsBadHex.test(value) ? NAN : +value); -} - -module.exports = throttle; - -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) - -/***/ }), - -/***/ "./node_modules/webpack/buildin/global.js": -/*!***********************************!*\ - !*** (webpack)/buildin/global.js ***! - \***********************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -var g; - -// This works in non-strict mode -g = (function() { - return this; -})(); - -try { - // This works if eval is allowed (see CSP) - g = g || Function("return this")() || (1, eval)("this"); -} catch (e) { - // This works if the window reference is available - if (typeof window === "object") g = window; -} - -// g can still be undefined, but nothing to do about it... -// We return undefined, instead of nothing here, so it's -// easier to handle this case. if(!global) { ...} - -module.exports = g; - - -/***/ }), - -/***/ "./preview-src/activeLineMarker.ts": -/*!*****************************************!*\ - !*** ./preview-src/activeLineMarker.ts ***! - \*****************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -const scroll_sync_1 = __webpack_require__(/*! ./scroll-sync */ "./preview-src/scroll-sync.ts"); -class ActiveLineMarker { - onDidChangeTextEditorSelection(line) { - const { previous } = scroll_sync_1.getElementsForSourceLine(line); - this._update(previous && previous.element); - } - _update(before) { - this._unmarkActiveElement(this._current); - this._markActiveElement(before); - this._current = before; - } - _unmarkActiveElement(element) { - if (!element) { - return; - } - element.className = element.className.replace(/\bcode-active-line\b/g, ''); - } - _markActiveElement(element) { - if (!element) { - return; - } - element.className += ' code-active-line'; - } -} -exports.ActiveLineMarker = ActiveLineMarker; - - -/***/ }), - -/***/ "./preview-src/events.ts": -/*!*******************************!*\ - !*** ./preview-src/events.ts ***! - \*******************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -function onceDocumentLoaded(f) { - if (document.readyState === 'loading' || document.readyState === 'uninitialized') { - document.addEventListener('DOMContentLoaded', f); - } - else { - f(); - } -} -exports.onceDocumentLoaded = onceDocumentLoaded; - - -/***/ }), - -/***/ "./preview-src/index.ts": -/*!******************************!*\ - !*** ./preview-src/index.ts ***! - \******************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const activeLineMarker_1 = __webpack_require__(/*! ./activeLineMarker */ "./preview-src/activeLineMarker.ts"); -const events_1 = __webpack_require__(/*! ./events */ "./preview-src/events.ts"); -const messaging_1 = __webpack_require__(/*! ./messaging */ "./preview-src/messaging.ts"); -const scroll_sync_1 = __webpack_require__(/*! ./scroll-sync */ "./preview-src/scroll-sync.ts"); -const settings_1 = __webpack_require__(/*! ./settings */ "./preview-src/settings.ts"); -const throttle = __webpack_require__(/*! lodash.throttle */ "./node_modules/lodash.throttle/index.js"); -let scrollDisabled = true; -const marker = new activeLineMarker_1.ActiveLineMarker(); -const settings = settings_1.getSettings(); -const vscode = acquireVsCodeApi(); -// Set VS Code state -let state = settings_1.getData('data-state'); -vscode.setState(state); -const messaging = messaging_1.createPosterForVsCode(vscode); -window.cspAlerter.setPoster(messaging); -window.styleLoadingMonitor.setPoster(messaging); -window.onload = () => { - updateImageSizes(); -}; -events_1.onceDocumentLoaded(() => { - if (settings.scrollPreviewWithEditor) { - setTimeout(() => { - // Try to scroll to fragment if available - if (state.fragment) { - const element = scroll_sync_1.getLineElementForFragment(state.fragment); - if (element) { - scrollDisabled = true; - scroll_sync_1.scrollToRevealSourceLine(element.line); - } - } - else { - const initialLine = +settings.line; - if (!isNaN(initialLine)) { - scrollDisabled = true; - scroll_sync_1.scrollToRevealSourceLine(initialLine); - } - } - }, 0); - } -}); -const onUpdateView = (() => { - const doScroll = throttle((line) => { - scrollDisabled = true; - scroll_sync_1.scrollToRevealSourceLine(line); - }, 50); - return (line, settings) => { - if (!isNaN(line)) { - settings.line = line; - doScroll(line); - } - }; -})(); -let updateImageSizes = throttle(() => { - const imageInfo = []; - let images = document.getElementsByTagName('img'); - if (images) { - let i; - for (i = 0; i < images.length; i++) { - const img = images[i]; - if (img.classList.contains('loading')) { - img.classList.remove('loading'); - } - imageInfo.push({ - id: img.id, - height: img.height, - width: img.width - }); - } - messaging.postMessage('cacheImageSizes', imageInfo); - } -}, 50); -window.addEventListener('resize', () => { - scrollDisabled = true; - updateImageSizes(); -}, true); -window.addEventListener('message', event => { - if (event.data.source !== settings.source) { - return; - } - switch (event.data.type) { - case 'onDidChangeTextEditorSelection': - marker.onDidChangeTextEditorSelection(event.data.line); - break; - case 'updateView': - onUpdateView(event.data.line, settings); - break; - } -}, false); -document.addEventListener('dblclick', event => { - if (!settings.doubleClickToSwitchToEditor) { - return; - } - // Ignore clicks on links - for (let node = event.target; node; node = node.parentNode) { - if (node.tagName === 'A') { - return; - } - } - const offset = event.pageY; - const line = scroll_sync_1.getEditorLineNumberForPageOffset(offset); - if (typeof line === 'number' && !isNaN(line)) { - messaging.postMessage('didClick', { line: Math.floor(line) }); - } -}); -document.addEventListener('click', event => { - if (!event) { - return; - } - let node = event.target; - while (node) { - if (node.tagName && node.tagName === 'A' && node.href) { - if (node.getAttribute('href').startsWith('#')) { - break; - } - if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:') || node.href.startsWith(settings.webviewResourceRoot)) { - const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').replace(new RegExp(`^${escapeRegExp(settings.webviewResourceRoot)}`)).split('#'); - messaging.postMessage('clickLink', { path, fragment }); - event.preventDefault(); - event.stopPropagation(); - break; - } - break; - } - node = node.parentNode; - } -}, true); -if (settings.scrollEditorWithPreview) { - window.addEventListener('scroll', throttle(() => { - if (scrollDisabled) { - scrollDisabled = false; - } - else { - const line = scroll_sync_1.getEditorLineNumberForPageOffset(window.scrollY); - if (typeof line === 'number' && !isNaN(line)) { - messaging.postMessage('revealLine', { line }); - state.line = line; - vscode.setState(state); - } - } - }, 50)); -} -function escapeRegExp(text) { - return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); -} - - -/***/ }), - -/***/ "./preview-src/messaging.ts": -/*!**********************************!*\ - !*** ./preview-src/messaging.ts ***! - \**********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const settings_1 = __webpack_require__(/*! ./settings */ "./preview-src/settings.ts"); -exports.createPosterForVsCode = (vscode) => { - return new class { - postMessage(type, body) { - vscode.postMessage({ - type, - source: settings_1.getSettings().source, - body - }); - } - }; -}; - - -/***/ }), - -/***/ "./preview-src/scroll-sync.ts": -/*!************************************!*\ - !*** ./preview-src/scroll-sync.ts ***! - \************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const settings_1 = __webpack_require__(/*! ./settings */ "./preview-src/settings.ts"); -function clamp(min, max, value) { - return Math.min(max, Math.max(min, value)); -} -function clampLine(line) { - return clamp(0, settings_1.getSettings().lineCount - 1, line); -} -const getCodeLineElements = (() => { - let elements; - return () => { - if (!elements) { - elements = [{ element: document.body, line: 0 }]; - for (const element of document.getElementsByClassName('code-line')) { - const line = +element.getAttribute('data-line'); - if (!isNaN(line)) { - elements.push({ element: element, line }); - } - } - } - return elements; - }; -})(); -/** - * Find the html elements that map to a specific target line in the editor. - * - * If an exact match, returns a single element. If the line is between elements, - * returns the element prior to and the element after the given line. - */ -function getElementsForSourceLine(targetLine) { - const lineNumber = Math.floor(targetLine); - const lines = getCodeLineElements(); - let previous = lines[0] || null; - for (const entry of lines) { - if (entry.line === lineNumber) { - return { previous: entry, next: undefined }; - } - else if (entry.line > lineNumber) { - return { previous, next: entry }; - } - previous = entry; - } - return { previous }; -} -exports.getElementsForSourceLine = getElementsForSourceLine; -/** - * Find the html elements that are at a specific pixel offset on the page. - */ -function getLineElementsAtPageOffset(offset) { - const lines = getCodeLineElements(); - const position = offset - window.scrollY; - let lo = -1; - let hi = lines.length - 1; - while (lo + 1 < hi) { - const mid = Math.floor((lo + hi) / 2); - const bounds = lines[mid].element.getBoundingClientRect(); - if (bounds.top + bounds.height >= position) { - hi = mid; - } - else { - lo = mid; - } - } - const hiElement = lines[hi]; - const hiBounds = hiElement.element.getBoundingClientRect(); - if (hi >= 1 && hiBounds.top > position) { - const loElement = lines[lo]; - return { previous: loElement, next: hiElement }; - } - return { previous: hiElement }; -} -exports.getLineElementsAtPageOffset = getLineElementsAtPageOffset; -/** - * Attempt to reveal the element for a source line in the editor. - */ -function scrollToRevealSourceLine(line) { - if (!settings_1.getSettings().scrollPreviewWithEditor) { - return; - } - if (line <= 0) { - window.scroll(window.scrollX, 0); - return; - } - const { previous, next } = getElementsForSourceLine(line); - if (!previous) { - return; - } - let scrollTo = 0; - const rect = previous.element.getBoundingClientRect(); - const previousTop = rect.top; - if (next && next.line !== previous.line) { - // Between two elements. Go to percentage offset between them. - const betweenProgress = (line - previous.line) / (next.line - previous.line); - const elementOffset = next.element.getBoundingClientRect().top - previousTop; - scrollTo = previousTop + betweenProgress * elementOffset; - } - else { - const progressInElement = line - Math.floor(line); - scrollTo = previousTop + (rect.height * progressInElement); - } - window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); -} -exports.scrollToRevealSourceLine = scrollToRevealSourceLine; -function getEditorLineNumberForPageOffset(offset) { - const { previous, next } = getLineElementsAtPageOffset(offset); - if (previous) { - const previousBounds = previous.element.getBoundingClientRect(); - const offsetFromPrevious = (offset - window.scrollY - previousBounds.top); - if (next) { - const progressBetweenElements = offsetFromPrevious / (next.element.getBoundingClientRect().top - previousBounds.top); - const line = previous.line + progressBetweenElements * (next.line - previous.line); - return clampLine(line); - } - else { - const progressWithinElement = offsetFromPrevious / (previousBounds.height); - const line = previous.line + progressWithinElement; - return clampLine(line); - } - } - return null; -} -exports.getEditorLineNumberForPageOffset = getEditorLineNumberForPageOffset; -/** - * Try to find the html element by using a fragment id - */ -function getLineElementForFragment(fragment) { - return getCodeLineElements().find((element) => { - return element.element.id === fragment; - }); -} -exports.getLineElementForFragment = getLineElementForFragment; - - -/***/ }), - -/***/ "./preview-src/settings.ts": -/*!*********************************!*\ - !*** ./preview-src/settings.ts ***! - \*********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -let cachedSettings = undefined; -function getData(key) { - const element = document.getElementById('vscode-markdown-preview-data'); - if (element) { - const data = element.getAttribute(key); - if (data) { - return JSON.parse(data); - } - } - throw new Error(`Could not load data for ${key}`); -} -exports.getData = getData; -function getSettings() { - if (cachedSettings) { - return cachedSettings; - } - cachedSettings = getData('data-settings'); - if (cachedSettings) { - return cachedSettings; - } - throw new Error('Could not load settings'); -} -exports.getSettings = getSettings; - - -/***/ }) - -/******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7OztBQ25CQTtBQUNBLDhDQUE4QyxjQUFjO0FBQzVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZUFBZSxXQUFXO0FBQzFCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQzlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOENBQThDLGNBQWM7QUFDNUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FDZEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhDQUE4QyxjQUFjO0FBQzVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7QUFDRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUJBQW1CLG1CQUFtQjtBQUN0QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBLENBQUM7QUFDRDtBQUNBO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQ0FBaUMsTUFBTTtBQUN2QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDJDQUEyQyx5QkFBeUI7QUFDcEU7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdIQUF3SCwyQ0FBMkM7QUFDbkssb0RBQW9ELGlCQUFpQjtBQUNyRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxREFBcUQsT0FBTztBQUM1RDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsZ0NBQWdDO0FBQ2hDOzs7Ozs7Ozs7Ozs7O0FDckpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEMsY0FBYztBQUM1RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQ2pCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOENBQThDLGNBQWM7QUFDNUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHlCQUF5QixrQ0FBa0M7QUFDM0Q7QUFDQTtBQUNBO0FBQ0EsbUNBQW1DLHlCQUF5QjtBQUM1RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQjtBQUNwQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnQkFBZ0I7QUFDaEI7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLGlCQUFpQjtBQUM1QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxpQkFBaUI7QUFDNUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBOzs7Ozs7Ozs7Ozs7O0FDdklBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEMsY0FBYztBQUM1RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQ0FBK0MsSUFBSTtBQUNuRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3ByZXZpZXctc3JjL2luZGV4LnRzXCIpO1xuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwidmFyIGc7XHJcblxyXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxyXG5nID0gKGZ1bmN0aW9uKCkge1xyXG5cdHJldHVybiB0aGlzO1xyXG59KSgpO1xyXG5cclxudHJ5IHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcclxuXHRnID0gZyB8fCBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCkgfHwgKDEsIGV2YWwpKFwidGhpc1wiKTtcclxufSBjYXRjaCAoZSkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXHJcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XHJcbn1cclxuXHJcbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cclxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3NcclxuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBnO1xyXG4iLCJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuY29uc3Qgc2Nyb2xsX3N5bmNfMSA9IHJlcXVpcmUoXCIuL3Njcm9sbC1zeW5jXCIpO1xuY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG4gICAgb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmUpIHtcbiAgICAgICAgY29uc3QgeyBwcmV2aW91cyB9ID0gc2Nyb2xsX3N5bmNfMS5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcbiAgICB9XG4gICAgX3VwZGF0ZShiZWZvcmUpIHtcbiAgICAgICAgdGhpcy5fdW5tYXJrQWN0aXZlRWxlbWVudCh0aGlzLl9jdXJyZW50KTtcbiAgICAgICAgdGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcbiAgICAgICAgdGhpcy5fY3VycmVudCA9IGJlZm9yZTtcbiAgICB9XG4gICAgX3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSA9IGVsZW1lbnQuY2xhc3NOYW1lLnJlcGxhY2UoL1xcYmNvZGUtYWN0aXZlLWxpbmVcXGIvZywgJycpO1xuICAgIH1cbiAgICBfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuICAgIH1cbn1cbmV4cG9ydHMuQWN0aXZlTGluZU1hcmtlciA9IEFjdGl2ZUxpbmVNYXJrZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGYpIHtcbiAgICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBmKCk7XG4gICAgfVxufVxuZXhwb3J0cy5vbmNlRG9jdW1lbnRMb2FkZWQgPSBvbmNlRG9jdW1lbnRMb2FkZWQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbi8vIFNldCBWUyBDb2RlIHN0YXRlXG5sZXQgc3RhdGUgPSBzZXR0aW5nc18xLmdldERhdGEoJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5jb25zdCBtZXNzYWdpbmcgPSBtZXNzYWdpbmdfMS5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuICAgIHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5ldmVudHNfMS5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuICAgIGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICBpZiAoc3RhdGUuZnJhZ21lbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHN0YXRlLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKGluaXRpYWxMaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDApO1xuICAgIH1cbn0pO1xuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcbiAgICBjb25zdCBkb1Njcm9sbCA9IHRocm90dGxlKChsaW5lKSA9PiB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgc2Nyb2xsX3N5bmNfMS5zY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG4gICAgfSwgNTApO1xuICAgIHJldHVybiAobGluZSwgc2V0dGluZ3MpID0+IHtcbiAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgc2V0dGluZ3MubGluZSA9IGxpbmU7XG4gICAgICAgICAgICBkb1Njcm9sbChsaW5lKTtcbiAgICAgICAgfVxuICAgIH07XG59KSgpO1xubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG4gICAgY29uc3QgaW1hZ2VJbmZvID0gW107XG4gICAgbGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcbiAgICBpZiAoaW1hZ2VzKSB7XG4gICAgICAgIGxldCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgaW1hZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBpbWcgPSBpbWFnZXNbaV07XG4gICAgICAgICAgICBpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG4gICAgICAgICAgICAgICAgaW1nLmNsYXNzTGlzdC5yZW1vdmUoJ2xvYWRpbmcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGltYWdlSW5mby5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogaW1nLmlkLFxuICAgICAgICAgICAgICAgIGhlaWdodDogaW1nLmhlaWdodCxcbiAgICAgICAgICAgICAgICB3aWR0aDogaW1nLndpZHRoXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG4gICAgfVxufSwgNTApO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcbiAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG59LCBmYWxzZSk7XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3NcbiAgICBmb3IgKGxldCBub2RlID0gZXZlbnQudGFyZ2V0OyBub2RlOyBub2RlID0gbm9kZS5wYXJlbnROb2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcbiAgICB9XG59KTtcbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuICAgIGlmICghZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgbm9kZSA9IGV2ZW50LnRhcmdldDtcbiAgICB3aGlsZSAobm9kZSkge1xuICAgICAgICBpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuICAgICAgICAgICAgaWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAobm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ2ZpbGU6Ly8nKSB8fCBub2RlLmhyZWYuc3RhcnRzV2l0aCgndnNjb2RlLXJlc291cmNlOicpIHx8IG5vZGUuaHJlZi5zdGFydHNXaXRoKHNldHRpbmdzLndlYnZpZXdSZXNvdXJjZVJvb3QpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgW3BhdGgsIGZyYWdtZW50XSA9IG5vZGUuaHJlZi5yZXBsYWNlKC9eKGZpbGU6XFwvXFwvfHZzY29kZS1yZXNvdXJjZTopL2ksICcnKS5yZXBsYWNlKG5ldyBSZWdFeHAoYF4ke2VzY2FwZVJlZ0V4cChzZXR0aW5ncy53ZWJ2aWV3UmVzb3VyY2VSb290KX1gKSkuc3BsaXQoJyMnKTtcbiAgICAgICAgICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NsaWNrTGluaycsIHsgcGF0aCwgZnJhZ21lbnQgfSk7XG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG4gICAgfVxufSwgdHJ1ZSk7XG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhyb3R0bGUoKCkgPT4ge1xuICAgICAgICBpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcbiAgICAgICAgICAgIHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gc2Nyb2xsX3N5bmNfMS5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG4gICAgICAgICAgICBpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgncmV2ZWFsTGluZScsIHsgbGluZSB9KTtcbiAgICAgICAgICAgICAgICBzdGF0ZS5saW5lID0gbGluZTtcbiAgICAgICAgICAgICAgICB2c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSwgNTApKTtcbn1cbmZ1bmN0aW9uIGVzY2FwZVJlZ0V4cCh0ZXh0KSB7XG4gICAgcmV0dXJuIHRleHQucmVwbGFjZSgvWy1bXFxde30oKSorPy4sXFxcXF4kfCNcXHNdL2csICdcXFxcJCYnKTtcbn1cbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5leHBvcnRzLmNyZWF0ZVBvc3RlckZvclZzQ29kZSA9ICh2c2NvZGUpID0+IHtcbiAgICByZXR1cm4gbmV3IGNsYXNzIHtcbiAgICAgICAgcG9zdE1lc3NhZ2UodHlwZSwgYm9keSkge1xuICAgICAgICAgICAgdnNjb2RlLnBvc3RNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICB0eXBlLFxuICAgICAgICAgICAgICAgIHNvdXJjZTogc2V0dGluZ3NfMS5nZXRTZXR0aW5ncygpLnNvdXJjZSxcbiAgICAgICAgICAgICAgICBib2R5XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH07XG59O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmZ1bmN0aW9uIGNsYW1wKG1pbiwgbWF4LCB2YWx1ZSkge1xuICAgIHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lKSB7XG4gICAgcmV0dXJuIGNsYW1wKDAsIHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuICAgIGxldCBlbGVtZW50cztcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBpZiAoIWVsZW1lbnRzKSB7XG4gICAgICAgICAgICBlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS1saW5lJykpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBsaW5lID0gK2VsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLWxpbmUnKTtcbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKGxpbmUpKSB7XG4gICAgICAgICAgICAgICAgICAgIGVsZW1lbnRzLnB1c2goeyBlbGVtZW50OiBlbGVtZW50LCBsaW5lIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZWxlbWVudHM7XG4gICAgfTtcbn0pKCk7XG4vKipcbiAqIEZpbmQgdGhlIGh0bWwgZWxlbWVudHMgdGhhdCBtYXAgdG8gYSBzcGVjaWZpYyB0YXJnZXQgbGluZSBpbiB0aGUgZWRpdG9yLlxuICpcbiAqIElmIGFuIGV4YWN0IG1hdGNoLCByZXR1cm5zIGEgc2luZ2xlIGVsZW1lbnQuIElmIHRoZSBsaW5lIGlzIGJldHdlZW4gZWxlbWVudHMsXG4gKiByZXR1cm5zIHRoZSBlbGVtZW50IHByaW9yIHRvIGFuZCB0aGUgZWxlbWVudCBhZnRlciB0aGUgZ2l2ZW4gbGluZS5cbiAqL1xuZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmUpIHtcbiAgICBjb25zdCBsaW5lTnVtYmVyID0gTWF0aC5mbG9vcih0YXJnZXRMaW5lKTtcbiAgICBjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcbiAgICBsZXQgcHJldmlvdXMgPSBsaW5lc1swXSB8fCBudWxsO1xuICAgIGZvciAoY29uc3QgZW50cnkgb2YgbGluZXMpIHtcbiAgICAgICAgaWYgKGVudHJ5LmxpbmUgPT09IGxpbmVOdW1iZXIpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHByZXZpb3VzOiBlbnRyeSwgbmV4dDogdW5kZWZpbmVkIH07XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoZW50cnkubGluZSA+IGxpbmVOdW1iZXIpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHByZXZpb3VzLCBuZXh0OiBlbnRyeSB9O1xuICAgICAgICB9XG4gICAgICAgIHByZXZpb3VzID0gZW50cnk7XG4gICAgfVxuICAgIHJldHVybiB7IHByZXZpb3VzIH07XG59XG5leHBvcnRzLmdldEVsZW1lbnRzRm9yU291cmNlTGluZSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZTtcbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IGFyZSBhdCBhIHNwZWNpZmljIHBpeGVsIG9mZnNldCBvbiB0aGUgcGFnZS5cbiAqL1xuZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCkge1xuICAgIGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuICAgIGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG4gICAgbGV0IGxvID0gLTE7XG4gICAgbGV0IGhpID0gbGluZXMubGVuZ3RoIC0gMTtcbiAgICB3aGlsZSAobG8gKyAxIDwgaGkpIHtcbiAgICAgICAgY29uc3QgbWlkID0gTWF0aC5mbG9vcigobG8gKyBoaSkgLyAyKTtcbiAgICAgICAgY29uc3QgYm91bmRzID0gbGluZXNbbWlkXS5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcbiAgICAgICAgICAgIGhpID0gbWlkO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgbG8gPSBtaWQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuICAgIGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgaWYgKGhpID49IDEgJiYgaGlCb3VuZHMudG9wID4gcG9zaXRpb24pIHtcbiAgICAgICAgY29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuICAgICAgICByZXR1cm4geyBwcmV2aW91czogbG9FbGVtZW50LCBuZXh0OiBoaUVsZW1lbnQgfTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcHJldmlvdXM6IGhpRWxlbWVudCB9O1xufVxuZXhwb3J0cy5nZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQgPSBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQ7XG4vKipcbiAqIEF0dGVtcHQgdG8gcmV2ZWFsIHRoZSBlbGVtZW50IGZvciBhIHNvdXJjZSBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKi9cbmZ1bmN0aW9uIHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lKSB7XG4gICAgaWYgKCFzZXR0aW5nc18xLmdldFNldHRpbmdzKCkuc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAobGluZSA8PSAwKSB7XG4gICAgICAgIHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIDApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcbiAgICBpZiAoIXByZXZpb3VzKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IHNjcm9sbFRvID0gMDtcbiAgICBjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICBjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuICAgIGlmIChuZXh0ICYmIG5leHQubGluZSAhPT0gcHJldmlvdXMubGluZSkge1xuICAgICAgICAvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuICAgICAgICBjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICBjb25zdCBlbGVtZW50T2Zmc2V0ID0gbmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzVG9wO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IHByb2dyZXNzSW5FbGVtZW50ID0gbGluZSAtIE1hdGguZmxvb3IobGluZSk7XG4gICAgICAgIHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG4gICAgfVxuICAgIHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIE1hdGgubWF4KDEsIHdpbmRvdy5zY3JvbGxZICsgc2Nyb2xsVG8pKTtcbn1cbmV4cG9ydHMuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lID0gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lO1xuZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHByZXZpb3VzKSB7XG4gICAgICAgIGNvbnN0IHByZXZpb3VzQm91bmRzID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgY29uc3Qgb2Zmc2V0RnJvbVByZXZpb3VzID0gKG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcbiAgICAgICAgaWYgKG5leHQpIHtcbiAgICAgICAgICAgIGNvbnN0IHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuICAgICAgICAgICAgY29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyAqIChuZXh0LmxpbmUgLSBwcmV2aW91cy5saW5lKTtcbiAgICAgICAgICAgIHJldHVybiBjbGFtcExpbmUobGluZSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc1dpdGhpbkVsZW1lbnQgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAocHJldmlvdXNCb3VuZHMuaGVpZ2h0KTtcbiAgICAgICAgICAgIGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50O1xuICAgICAgICAgICAgcmV0dXJuIGNsYW1wTGluZShsaW5lKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbn1cbmV4cG9ydHMuZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldDtcbi8qKlxuICogVHJ5IHRvIGZpbmQgdGhlIGh0bWwgZWxlbWVudCBieSB1c2luZyBhIGZyYWdtZW50IGlkXG4gKi9cbmZ1bmN0aW9uIGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQoZnJhZ21lbnQpIHtcbiAgICByZXR1cm4gZ2V0Q29kZUxpbmVFbGVtZW50cygpLmZpbmQoKGVsZW1lbnQpID0+IHtcbiAgICAgICAgcmV0dXJuIGVsZW1lbnQuZWxlbWVudC5pZCA9PT0gZnJhZ21lbnQ7XG4gICAgfSk7XG59XG5leHBvcnRzLmdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQgPSBnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var i=t[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:o})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=11)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let o=void 0;function i(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=i,t.getSettings=function(){if(o)return o;if(o=i("data-settings"))return o;throw new Error("Could not load settings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);function i(e){return t=0,n=o.getSettings().lineCount-1,i=e,Math.min(n,Math.max(t,i));var t,n,i}const r=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName("code-line")){const n=+t.getAttribute("data-line");isNaN(n)||e.push({element:t,line:n})}}return e}})();function s(e){const t=Math.floor(e),n=r();let o=n[0]||null;for(const e of n){if(e.line===t)return{previous:e,next:void 0};if(e.line>t)return{previous:o,next:e};o=e}return{previous:o}}function c(e){const t=r(),n=e-window.scrollY;let o=-1,i=t.length-1;for(;o+1=n?i=e:o=e}const s=t[i],c=s.element.getBoundingClientRect();if(i>=1&&c.top>n){return{previous:t[o],next:s}}return{previous:s}}t.getElementsForSourceLine=s,t.getLineElementsAtPageOffset=c,t.scrollToRevealSourceLine=function(e){if(!o.getSettings().scrollPreviewWithEditor)return;if(e<=0)return void window.scroll(window.scrollX,0);const{previous:t,next:n}=s(e);if(!t)return;let i=0;const r=t.element.getBoundingClientRect(),c=r.top;if(n&&n.line!==t.line)i=c+(e-t.line)/(n.line-t.line)*(n.element.getBoundingClientRect().top-c);else{const t=e-Math.floor(e);i=c+r.height*t}window.scroll(window.scrollX,Math.max(1,window.scrollY+i))},t.getEditorLineNumberForPageOffset=function(e){const{previous:t,next:n}=c(e);if(t){const o=t.element.getBoundingClientRect(),r=e-window.scrollY-o.top;if(n){const e=r/(n.element.getBoundingClientRect().top-o.top);return i(t.line+e*(n.line-t.line))}{const e=r/o.height;return i(t.line+e)}}return null},t.getLineElementForFragment=function(e){return r().find(t=>t.element.id===e)}},,,,,function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){(function(t){var n="Expected a function",o=NaN,i="[object Symbol]",r=/^\s+|\s+$/g,s=/^[-+]0x[0-9a-f]+$/i,c=/^0b[01]+$/i,a=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,d="object"==typeof self&&self&&self.Object===Object&&self,f=l||d||Function("return this")(),g=Object.prototype.toString,p=Math.max,m=Math.min,v=function(){return f.Date.now()};function h(e,t,o){var i,r,s,c,a,u,l=0,d=!1,f=!1,g=!0;if("function"!=typeof e)throw new TypeError(n);function h(t){var n=i,o=r;return i=r=void 0,l=t,c=e.apply(o,n)}function y(e){var n=e-u;return void 0===u||n>=t||n<0||f&&e-l>=s}function E(){var e=v();if(y(e))return L(e);a=setTimeout(E,function(e){var n=t-(e-u);return f?m(n,s-(e-l)):n}(e))}function L(e){return a=void 0,g&&i?h(e):(i=r=void 0,c)}function M(){var e=v(),n=y(e);if(i=arguments,r=this,u=e,n){if(void 0===a)return function(e){return l=e,a=setTimeout(E,t),d?h(e):c}(u);if(f)return a=setTimeout(E,t),h(u)}return void 0===a&&(a=setTimeout(E,t)),c}return t=b(t)||0,w(o)&&(d=!!o.leading,s=(f="maxWait"in o)?p(b(o.maxWait)||0,t):s,g="trailing"in o?!!o.trailing:g),M.cancel=function(){void 0!==a&&clearTimeout(a),l=0,i=u=r=a=void 0},M.flush=function(){return void 0===a?c:L(v())},M}function w(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function b(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&g.call(e)==i}(e))return o;if(w(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=w(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(r,"");var n=c.test(e);return n||a.test(e)?u(e.slice(2),n?2:8):s.test(e)?o:+e}e.exports=function(e,t,o){var i=!0,r=!0;if("function"!=typeof e)throw new TypeError(n);return w(o)&&(i="leading"in o?!!o.leading:i,r="trailing"in o?!!o.trailing:r),h(e,t,{leading:i,maxWait:t,trailing:r})}}).call(this,n(6))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);t.createPosterForVsCode=(e=>new class{postMessage(t,n){e.postMessage({type:t,source:o.getSettings().source,body:n})}})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.onceDocumentLoaded=function(e){"loading"===document.readyState||"uninitialized"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(1);t.ActiveLineMarker=class{onDidChangeTextEditorSelection(e){const{previous:t}=o.getElementsForSourceLine(e);this._update(t&&t.element)}_update(e){this._unmarkActiveElement(this._current),this._markActiveElement(e),this._current=e}_unmarkActiveElement(e){e&&(e.className=e.className.replace(/\bcode-active-line\b/g,""))}_markActiveElement(e){e&&(e.className+=" code-active-line")}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(10),i=n(9),r=n(8),s=n(1),c=n(0),a=n(7);let u=!0;const l=new o.ActiveLineMarker,d=c.getSettings(),f=acquireVsCodeApi();let g=c.getData("data-state");f.setState(g);const p=r.createPosterForVsCode(f);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=(()=>{v()}),i.onceDocumentLoaded(()=>{d.scrollPreviewWithEditor&&setTimeout(()=>{if(g.fragment){const e=s.getLineElementForFragment(g.fragment);e&&(u=!0,s.scrollToRevealSourceLine(e.line))}else{const e=+d.line;isNaN(e)||(u=!0,s.scrollToRevealSourceLine(e))}},0)});const m=(()=>{const e=a(e=>{u=!0,s.scrollToRevealSourceLine(e)},50);return(t,n)=>{isNaN(t)||(n.line=t,e(t))}})();let v=a(()=>{const e=[];let t=document.getElementsByTagName("img");if(t){let n;for(n=0;n{u=!0,v()},!0),window.addEventListener("message",e=>{if(e.data.source===d.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":m(e.data.line,d)}},!1),document.addEventListener("dblclick",e=>{if(!d.doubleClickToSwitchToEditor)return;for(let t=e.target;t;t=t.parentNode)if("A"===t.tagName)return;const t=e.pageY,n=s.getEditorLineNumberForPageOffset(t);"number"!=typeof n||isNaN(n)||p.postMessage("didClick",{line:Math.floor(n)})});const h=["http:","https:","mailto:","vscode:","vscode-insiders"];document.addEventListener("click",e=>{if(!e)return;let t=e.target;for(;t;){if(t.tagName&&"A"===t.tagName&&t.href){if(t.getAttribute("href").startsWith("#"))return;if(h.some(e=>t.href.startsWith(e)))return;const n=t.getAttribute("data-href")||t.getAttribute("href");return/^[a-z\-]+:/i.test(n)?void 0:(p.postMessage("openLink",{href:n}),e.preventDefault(),void e.stopPropagation())}t=t.parentNode}},!0),window.addEventListener("scroll",a(()=>{if(u)u=!1;else{const e=s.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||(p.postMessage("revealLine",{line:e}),g.line=e,f.setState(g))}},50))}]); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2Nyb2xsLXN5bmMudHMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9kYXNoLnRocm90dGxlL2luZGV4LmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9ldmVudHMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvYWN0aXZlTGluZU1hcmtlci50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9pbmRleC50cyJdLCJuYW1lcyI6WyJpbnN0YWxsZWRNb2R1bGVzIiwiX193ZWJwYWNrX3JlcXVpcmVfXyIsIm1vZHVsZUlkIiwiZXhwb3J0cyIsIm1vZHVsZSIsImkiLCJsIiwibW9kdWxlcyIsImNhbGwiLCJtIiwiYyIsImQiLCJuYW1lIiwiZ2V0dGVyIiwibyIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiY29uZmlndXJhYmxlIiwiZW51bWVyYWJsZSIsImdldCIsInIiLCJ2YWx1ZSIsIm4iLCJfX2VzTW9kdWxlIiwib2JqZWN0IiwicHJvcGVydHkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsInAiLCJzIiwiY2FjaGVkU2V0dGluZ3MiLCJ1bmRlZmluZWQiLCJnZXREYXRhIiwia2V5IiwiZWxlbWVudCIsImRvY3VtZW50IiwiZ2V0RWxlbWVudEJ5SWQiLCJkYXRhIiwiZ2V0QXR0cmlidXRlIiwiSlNPTiIsInBhcnNlIiwiRXJyb3IiLCJnZXRTZXR0aW5ncyIsInNldHRpbmdzXzEiLCJjbGFtcExpbmUiLCJsaW5lIiwibWluIiwibWF4IiwibGluZUNvdW50IiwiTWF0aCIsImdldENvZGVMaW5lRWxlbWVudHMiLCJlbGVtZW50cyIsImJvZHkiLCJnZXRFbGVtZW50c0J5Q2xhc3NOYW1lIiwiaXNOYU4iLCJwdXNoIiwiZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lIiwidGFyZ2V0TGluZSIsImxpbmVOdW1iZXIiLCJmbG9vciIsImxpbmVzIiwicHJldmlvdXMiLCJlbnRyeSIsIm5leHQiLCJnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQiLCJvZmZzZXQiLCJwb3NpdGlvbiIsIndpbmRvdyIsInNjcm9sbFkiLCJsbyIsImhpIiwibGVuZ3RoIiwibWlkIiwiYm91bmRzIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidG9wIiwiaGVpZ2h0IiwiaGlFbGVtZW50IiwiaGlCb3VuZHMiLCJzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUiLCJzY3JvbGxQcmV2aWV3V2l0aEVkaXRvciIsInNjcm9sbCIsInNjcm9sbFgiLCJzY3JvbGxUbyIsInJlY3QiLCJwcmV2aW91c1RvcCIsInByb2dyZXNzSW5FbGVtZW50IiwiZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQiLCJwcmV2aW91c0JvdW5kcyIsIm9mZnNldEZyb21QcmV2aW91cyIsInByb2dyZXNzQmV0d2VlbkVsZW1lbnRzIiwicHJvZ3Jlc3NXaXRoaW5FbGVtZW50IiwiZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCIsImZyYWdtZW50IiwiZmluZCIsImlkIiwiZyIsInRoaXMiLCJGdW5jdGlvbiIsImV2YWwiLCJlIiwiZ2xvYmFsIiwiRlVOQ19FUlJPUl9URVhUIiwiTkFOIiwic3ltYm9sVGFnIiwicmVUcmltIiwicmVJc0JhZEhleCIsInJlSXNCaW5hcnkiLCJyZUlzT2N0YWwiLCJmcmVlUGFyc2VJbnQiLCJwYXJzZUludCIsImZyZWVHbG9iYWwiLCJmcmVlU2VsZiIsInNlbGYiLCJyb290Iiwib2JqZWN0VG9TdHJpbmciLCJ0b1N0cmluZyIsIm5hdGl2ZU1heCIsIm5hdGl2ZU1pbiIsIm5vdyIsIkRhdGUiLCJkZWJvdW5jZSIsImZ1bmMiLCJ3YWl0Iiwib3B0aW9ucyIsImxhc3RBcmdzIiwibGFzdFRoaXMiLCJtYXhXYWl0IiwicmVzdWx0IiwidGltZXJJZCIsImxhc3RDYWxsVGltZSIsImxhc3RJbnZva2VUaW1lIiwibGVhZGluZyIsIm1heGluZyIsInRyYWlsaW5nIiwiVHlwZUVycm9yIiwiaW52b2tlRnVuYyIsInRpbWUiLCJhcmdzIiwidGhpc0FyZyIsImFwcGx5Iiwic2hvdWxkSW52b2tlIiwidGltZVNpbmNlTGFzdENhbGwiLCJ0aW1lckV4cGlyZWQiLCJ0cmFpbGluZ0VkZ2UiLCJzZXRUaW1lb3V0IiwicmVtYWluaW5nV2FpdCIsImRlYm91bmNlZCIsImlzSW52b2tpbmciLCJhcmd1bWVudHMiLCJsZWFkaW5nRWRnZSIsInRvTnVtYmVyIiwiaXNPYmplY3QiLCJjYW5jZWwiLCJjbGVhclRpbWVvdXQiLCJmbHVzaCIsInR5cGUiLCJpc09iamVjdExpa2UiLCJpc1N5bWJvbCIsIm90aGVyIiwidmFsdWVPZiIsInJlcGxhY2UiLCJpc0JpbmFyeSIsInRlc3QiLCJzbGljZSIsImNyZWF0ZVBvc3RlckZvclZzQ29kZSIsInZzY29kZSIsIltvYmplY3QgT2JqZWN0XSIsInBvc3RNZXNzYWdlIiwic291cmNlIiwib25jZURvY3VtZW50TG9hZGVkIiwiZiIsInJlYWR5U3RhdGUiLCJhZGRFdmVudExpc3RlbmVyIiwic2Nyb2xsX3N5bmNfMSIsIkFjdGl2ZUxpbmVNYXJrZXIiLCJfdXBkYXRlIiwiYmVmb3JlIiwiX3VubWFya0FjdGl2ZUVsZW1lbnQiLCJfY3VycmVudCIsIl9tYXJrQWN0aXZlRWxlbWVudCIsImNsYXNzTmFtZSIsImFjdGl2ZUxpbmVNYXJrZXJfMSIsImV2ZW50c18xIiwibWVzc2FnaW5nXzEiLCJ0aHJvdHRsZSIsInNjcm9sbERpc2FibGVkIiwibWFya2VyIiwic2V0dGluZ3MiLCJhY3F1aXJlVnNDb2RlQXBpIiwic3RhdGUiLCJzZXRTdGF0ZSIsIm1lc3NhZ2luZyIsImNzcEFsZXJ0ZXIiLCJzZXRQb3N0ZXIiLCJzdHlsZUxvYWRpbmdNb25pdG9yIiwib25sb2FkIiwidXBkYXRlSW1hZ2VTaXplcyIsImluaXRpYWxMaW5lIiwib25VcGRhdGVWaWV3IiwiZG9TY3JvbGwiLCJpbWFnZUluZm8iLCJpbWFnZXMiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsImltZyIsImNsYXNzTGlzdCIsImNvbnRhaW5zIiwicmVtb3ZlIiwid2lkdGgiLCJldmVudCIsIm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbiIsImRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvciIsIm5vZGUiLCJ0YXJnZXQiLCJwYXJlbnROb2RlIiwidGFnTmFtZSIsInBhZ2VZIiwicGFzc1Rocm91Z2hMaW5rU2NoZW1lcyIsImhyZWYiLCJzdGFydHNXaXRoIiwic29tZSIsInNjaGVtZSIsImhyZWZUZXh0IiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iXSwibWFwcGluZ3MiOiJhQUNBLElBQUFBLEtBR0EsU0FBQUMsRUFBQUMsR0FHQSxHQUFBRixFQUFBRSxHQUNBLE9BQUFGLEVBQUFFLEdBQUFDLFFBR0EsSUFBQUMsRUFBQUosRUFBQUUsSUFDQUcsRUFBQUgsRUFDQUksR0FBQSxFQUNBSCxZQVVBLE9BTkFJLEVBQUFMLEdBQUFNLEtBQUFKLEVBQUFELFFBQUFDLElBQUFELFFBQUFGLEdBR0FHLEVBQUFFLEdBQUEsRUFHQUYsRUFBQUQsUUFLQUYsRUFBQVEsRUFBQUYsRUFHQU4sRUFBQVMsRUFBQVYsRUFHQUMsRUFBQVUsRUFBQSxTQUFBUixFQUFBUyxFQUFBQyxHQUNBWixFQUFBYSxFQUFBWCxFQUFBUyxJQUNBRyxPQUFBQyxlQUFBYixFQUFBUyxHQUNBSyxjQUFBLEVBQ0FDLFlBQUEsRUFDQUMsSUFBQU4sS0FNQVosRUFBQW1CLEVBQUEsU0FBQWpCLEdBQ0FZLE9BQUFDLGVBQUFiLEVBQUEsY0FBaURrQixPQUFBLEtBSWpEcEIsRUFBQXFCLEVBQUEsU0FBQWxCLEdBQ0EsSUFBQVMsRUFBQVQsS0FBQW1CLFdBQ0EsV0FBMkIsT0FBQW5CLEVBQUEsU0FDM0IsV0FBaUMsT0FBQUEsR0FFakMsT0FEQUgsRUFBQVUsRUFBQUUsRUFBQSxJQUFBQSxHQUNBQSxHQUlBWixFQUFBYSxFQUFBLFNBQUFVLEVBQUFDLEdBQXNELE9BQUFWLE9BQUFXLFVBQUFDLGVBQUFuQixLQUFBZ0IsRUFBQUMsSUFHdER4QixFQUFBMkIsRUFBQSxHQUlBM0IsSUFBQTRCLEVBQUEsbUNDOURBZCxPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQUM5QyxJQUFBUyxPQUFBQyxFQUNBLFNBQUFDLEVBQUFDLEdBQ0EsTUFBQUMsRUFBQUMsU0FBQUMsZUFBQSxnQ0FDQSxHQUFBRixFQUFBLENBQ0EsTUFBQUcsRUFBQUgsRUFBQUksYUFBQUwsR0FDQSxHQUFBSSxFQUNBLE9BQUFFLEtBQUFDLE1BQUFILEdBR0EsVUFBQUksaUNBQStDUixLQUUvQzlCLEVBQUE2QixVQVdBN0IsRUFBQXVDLFlBVkEsV0FDQSxHQUFBWixFQUNBLE9BQUFBLEVBR0EsR0FEQUEsRUFBQUUsRUFBQSxpQkFFQSxPQUFBRixFQUVBLFVBQUFXLE1BQUEsMERDckJBMUIsT0FBQUMsZUFBQWIsRUFBQSxjQUE4Q2tCLE9BQUEsSUFDOUMsTUFBQXNCLEVBQUExQyxFQUFBLEdBSUEsU0FBQTJDLEVBQUFDLEdBQ0EsT0FKQUMsRUFJQSxFQUpBQyxFQUlBSixFQUFBRCxjQUFBTSxVQUFBLEVBSkEzQixFQUlBd0IsRUFIQUksS0FBQUgsSUFBQUMsRUFBQUUsS0FBQUYsSUFBQUQsRUFBQXpCLElBREEsSUFBQXlCLEVBQUFDLEVBQUExQixFQU1BLE1BQUE2QixFQUFBLE1BQ0EsSUFBQUMsRUFDQSxXQUNBLElBQUFBLEVBQUEsQ0FDQUEsSUFBeUJqQixRQUFBQyxTQUFBaUIsS0FBQVAsS0FBQSxJQUN6QixVQUFBWCxLQUFBQyxTQUFBa0IsdUJBQUEsY0FDQSxNQUFBUixHQUFBWCxFQUFBSSxhQUFBLGFBQ0FnQixNQUFBVCxJQUNBTSxFQUFBSSxNQUFtQ3JCLFVBQUFXLFVBSW5DLE9BQUFNLElBWkEsR0FxQkEsU0FBQUssRUFBQUMsR0FDQSxNQUFBQyxFQUFBVCxLQUFBVSxNQUFBRixHQUNBRyxFQUFBVixJQUNBLElBQUFXLEVBQUFELEVBQUEsU0FDQSxVQUFBRSxLQUFBRixFQUFBLENBQ0EsR0FBQUUsRUFBQWpCLE9BQUFhLEVBQ0EsT0FBb0JHLFNBQUFDLEVBQUFDLFVBQUFoQyxHQUVwQixHQUFBK0IsRUFBQWpCLEtBQUFhLEVBQ0EsT0FBb0JHLFdBQUFFLEtBQUFELEdBRXBCRCxFQUFBQyxFQUVBLE9BQVlELFlBTVosU0FBQUcsRUFBQUMsR0FDQSxNQUFBTCxFQUFBVixJQUNBZ0IsRUFBQUQsRUFBQUUsT0FBQUMsUUFDQSxJQUFBQyxHQUFBLEVBQ0FDLEVBQUFWLEVBQUFXLE9BQUEsRUFDQSxLQUFBRixFQUFBLEVBQUFDLEdBQUEsQ0FDQSxNQUFBRSxFQUFBdkIsS0FBQVUsT0FBQVUsRUFBQUMsR0FBQSxHQUNBRyxFQUFBYixFQUFBWSxHQUFBdEMsUUFBQXdDLHdCQUNBRCxFQUFBRSxJQUFBRixFQUFBRyxRQUFBVixFQUNBSSxFQUFBRSxFQUdBSCxFQUFBRyxFQUdBLE1BQUFLLEVBQUFqQixFQUFBVSxHQUNBUSxFQUFBRCxFQUFBM0MsUUFBQXdDLHdCQUNBLEdBQUFKLEdBQUEsR0FBQVEsRUFBQUgsSUFBQVQsRUFBQSxDQUVBLE9BQWdCTCxTQURoQkQsRUFBQVMsR0FDZ0JOLEtBQUFjLEdBRWhCLE9BQVloQixTQUFBZ0IsR0F6QloxRSxFQUFBcUQsMkJBMkJBckQsRUFBQTZELDhCQStCQTdELEVBQUE0RSx5QkEzQkEsU0FBQWxDLEdBQ0EsSUFBQUYsRUFBQUQsY0FBQXNDLHdCQUNBLE9BRUEsR0FBQW5DLEdBQUEsRUFFQSxZQURBc0IsT0FBQWMsT0FBQWQsT0FBQWUsUUFBQSxHQUdBLE1BQUFyQixTQUFXQSxFQUFBRSxRQUFpQlAsRUFBQVgsR0FDNUIsSUFBQWdCLEVBQ0EsT0FFQSxJQUFBc0IsRUFBQSxFQUNBLE1BQUFDLEVBQUF2QixFQUFBM0IsUUFBQXdDLHdCQUNBVyxFQUFBRCxFQUFBVCxJQUNBLEdBQUFaLEtBQUFsQixPQUFBZ0IsRUFBQWhCLEtBSUFzQyxFQUFBRSxHQUZBeEMsRUFBQWdCLEVBQUFoQixPQUFBa0IsRUFBQWxCLEtBQUFnQixFQUFBaEIsT0FDQWtCLEVBQUE3QixRQUFBd0Msd0JBQUFDLElBQUFVLE9BR0EsQ0FDQSxNQUFBQyxFQUFBekMsRUFBQUksS0FBQVUsTUFBQWQsR0FDQXNDLEVBQUFFLEVBQUFELEVBQUFSLE9BQUFVLEVBRUFuQixPQUFBYyxPQUFBZCxPQUFBZSxRQUFBakMsS0FBQUYsSUFBQSxFQUFBb0IsT0FBQUMsUUFBQWUsS0FxQkFoRixFQUFBb0YsaUNBbEJBLFNBQUF0QixHQUNBLE1BQUFKLFNBQVdBLEVBQUFFLFFBQWlCQyxFQUFBQyxHQUM1QixHQUFBSixFQUFBLENBQ0EsTUFBQTJCLEVBQUEzQixFQUFBM0IsUUFBQXdDLHdCQUNBZSxFQUFBeEIsRUFBQUUsT0FBQUMsUUFBQW9CLEVBQUFiLElBQ0EsR0FBQVosRUFBQSxDQUNBLE1BQUEyQixFQUFBRCxHQUFBMUIsRUFBQTdCLFFBQUF3Qyx3QkFBQUMsSUFBQWEsRUFBQWIsS0FFQSxPQUFBL0IsRUFEQWlCLEVBQUFoQixLQUFBNkMsR0FBQTNCLEVBQUFsQixLQUFBZ0IsRUFBQWhCLE9BR0EsQ0FDQSxNQUFBOEMsRUFBQUYsRUFBQUQsRUFBQSxPQUVBLE9BQUE1QyxFQURBaUIsRUFBQWhCLEtBQUE4QyxJQUlBLGFBV0F4RixFQUFBeUYsMEJBTEEsU0FBQUMsR0FDQSxPQUFBM0MsSUFBQTRDLEtBQUE1RCxHQUNBQSxVQUFBNkQsS0FBQUYsdUJDcElBLElBQUFHLEVBR0FBLEVBQUEsV0FDQSxPQUFBQyxLQURBLEdBSUEsSUFFQUQsS0FBQUUsU0FBQSxjQUFBQSxLQUFBLEVBQUFDLE1BQUEsUUFDQyxNQUFBQyxHQUVELGlCQUFBakMsU0FBQTZCLEVBQUE3QixRQU9BL0QsRUFBQUQsUUFBQTZGLG9CQ25CQSxTQUFBSyxHQVVBLElBQUFDLEVBQUEsc0JBR0FDLEVBQUEsSUFHQUMsRUFBQSxrQkFHQUMsRUFBQSxhQUdBQyxFQUFBLHFCQUdBQyxFQUFBLGFBR0FDLEVBQUEsY0FHQUMsRUFBQUMsU0FHQUMsRUFBQSxpQkFBQVYsUUFBQXRGLGlCQUFBc0YsRUFHQVcsRUFBQSxpQkFBQUMsaUJBQUFsRyxpQkFBQWtHLEtBR0FDLEVBQUFILEdBQUFDLEdBQUFkLFNBQUEsY0FBQUEsR0FVQWlCLEVBUEFwRyxPQUFBVyxVQU9BMEYsU0FHQUMsRUFBQXBFLEtBQUFGLElBQ0F1RSxFQUFBckUsS0FBQUgsSUFrQkF5RSxFQUFBLFdBQ0EsT0FBQUwsRUFBQU0sS0FBQUQsT0F5REEsU0FBQUUsRUFBQUMsRUFBQUMsRUFBQUMsR0FDQSxJQUFBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUFBLEVBQ0FDLEdBQUEsRUFDQUMsR0FBQSxFQUNBQyxHQUFBLEVBRUEsc0JBQUFaLEVBQ0EsVUFBQWEsVUFBQWpDLEdBVUEsU0FBQWtDLEVBQUFDLEdBQ0EsSUFBQUMsRUFBQWIsRUFDQWMsRUFBQWIsRUFLQSxPQUhBRCxFQUFBQyxPQUFBL0YsRUFDQW9HLEVBQUFNLEVBQ0FULEVBQUFOLEVBQUFrQixNQUFBRCxFQUFBRCxHQXFCQSxTQUFBRyxFQUFBSixHQUNBLElBQUFLLEVBQUFMLEVBQUFQLEVBTUEsWUFBQW5HLElBQUFtRyxHQUFBWSxHQUFBbkIsR0FDQW1CLEVBQUEsR0FBQVQsR0FOQUksRUFBQU4sR0FNQUosRUFHQSxTQUFBZ0IsSUFDQSxJQUFBTixFQUFBbEIsSUFDQSxHQUFBc0IsRUFBQUosR0FDQSxPQUFBTyxFQUFBUCxHQUdBUixFQUFBZ0IsV0FBQUYsRUF6QkEsU0FBQU4sR0FDQSxJQUVBVCxFQUFBTCxHQUZBYyxFQUFBUCxHQUlBLE9BQUFHLEVBQUFmLEVBQUFVLEVBQUFELEdBSEFVLEVBQUFOLElBR0FILEVBb0JBa0IsQ0FBQVQsSUFHQSxTQUFBTyxFQUFBUCxHQUtBLE9BSkFSLE9BQUFsRyxFQUlBdUcsR0FBQVQsRUFDQVcsRUFBQUMsSUFFQVosRUFBQUMsT0FBQS9GLEVBQ0FpRyxHQWVBLFNBQUFtQixJQUNBLElBQUFWLEVBQUFsQixJQUNBNkIsRUFBQVAsRUFBQUosR0FNQSxHQUpBWixFQUFBd0IsVUFDQXZCLEVBQUE3QixLQUNBaUMsRUFBQU8sRUFFQVcsRUFBQSxDQUNBLFFBQUFySCxJQUFBa0csRUFDQSxPQXZFQSxTQUFBUSxHQU1BLE9BSkFOLEVBQUFNLEVBRUFSLEVBQUFnQixXQUFBRixFQUFBcEIsR0FFQVMsRUFBQUksRUFBQUMsR0FBQVQsRUFpRUFzQixDQUFBcEIsR0FFQSxHQUFBRyxFQUdBLE9BREFKLEVBQUFnQixXQUFBRixFQUFBcEIsR0FDQWEsRUFBQU4sR0FNQSxZQUhBbkcsSUFBQWtHLElBQ0FBLEVBQUFnQixXQUFBRixFQUFBcEIsSUFFQUssRUFJQSxPQXhHQUwsRUFBQTRCLEVBQUE1QixJQUFBLEVBQ0E2QixFQUFBNUIsS0FDQVEsSUFBQVIsRUFBQVEsUUFFQUwsR0FEQU0sRUFBQSxZQUFBVCxHQUNBUCxFQUFBa0MsRUFBQTNCLEVBQUFHLFVBQUEsRUFBQUosR0FBQUksRUFDQU8sRUFBQSxhQUFBVixNQUFBVSxZQWlHQWEsRUFBQU0sT0FuQ0EsZ0JBQ0ExSCxJQUFBa0csR0FDQXlCLGFBQUF6QixHQUVBRSxFQUFBLEVBQ0FOLEVBQUFLLEVBQUFKLEVBQUFHLE9BQUFsRyxHQStCQW9ILEVBQUFRLE1BNUJBLFdBQ0EsWUFBQTVILElBQUFrRyxFQUFBRCxFQUFBZ0IsRUFBQXpCLE1BNEJBNEIsRUEwRkEsU0FBQUssRUFBQW5JLEdBQ0EsSUFBQXVJLFNBQUF2SSxFQUNBLFFBQUFBLElBQUEsVUFBQXVJLEdBQUEsWUFBQUEsR0E0RUEsU0FBQUwsRUFBQWxJLEdBQ0Esb0JBQUFBLEVBQ0EsT0FBQUEsRUFFQSxHQWhDQSxTQUFBQSxHQUNBLHVCQUFBQSxHQXRCQSxTQUFBQSxHQUNBLFFBQUFBLEdBQUEsaUJBQUFBLEVBc0JBd0ksQ0FBQXhJLElBQUE4RixFQUFBM0csS0FBQWEsSUFBQW1GLEVBOEJBc0QsQ0FBQXpJLEdBQ0EsT0FBQWtGLEVBRUEsR0FBQWlELEVBQUFuSSxHQUFBLENBQ0EsSUFBQTBJLEVBQUEsbUJBQUExSSxFQUFBMkksUUFBQTNJLEVBQUEySSxVQUFBM0ksRUFDQUEsRUFBQW1JLEVBQUFPLEtBQUEsR0FBQUEsRUFFQSxvQkFBQTFJLEVBQ0EsV0FBQUEsT0FFQUEsSUFBQTRJLFFBQUF4RCxFQUFBLElBQ0EsSUFBQXlELEVBQUF2RCxFQUFBd0QsS0FBQTlJLEdBQ0EsT0FBQTZJLEdBQUF0RCxFQUFBdUQsS0FBQTlJLEdBQ0F3RixFQUFBeEYsRUFBQStJLE1BQUEsR0FBQUYsRUFBQSxLQUNBeEQsRUFBQXlELEtBQUE5SSxHQUFBa0YsR0FBQWxGLEVBR0FqQixFQUFBRCxRQTlJQSxTQUFBdUgsRUFBQUMsRUFBQUMsR0FDQSxJQUFBUSxHQUFBLEVBQ0FFLEdBQUEsRUFFQSxzQkFBQVosRUFDQSxVQUFBYSxVQUFBakMsR0FNQSxPQUpBa0QsRUFBQTVCLEtBQ0FRLEVBQUEsWUFBQVIsTUFBQVEsVUFDQUUsRUFBQSxhQUFBVixNQUFBVSxZQUVBYixFQUFBQyxFQUFBQyxHQUNBUyxVQUNBTCxRQUFBSixFQUNBVyw4RENqVEF2SCxPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQUM5QyxNQUFBc0IsRUFBQTFDLEVBQUEsR0FDQUUsRUFBQWtLLHNCQUFBLENBQUFDLEdBQ0EsVUFDQUMsWUFBQVgsRUFBQXhHLEdBQ0FrSCxFQUFBRSxhQUNBWixPQUNBYSxPQUFBOUgsRUFBQUQsY0FBQStILE9BQ0FySCwwQ0NSQXJDLE9BQUFDLGVBQUFiLEVBQUEsY0FBOENrQixPQUFBLElBUzlDbEIsRUFBQXVLLG1CQVJBLFNBQUFDLEdBQ0EsWUFBQXhJLFNBQUF5SSxZQUFBLGtCQUFBekksU0FBQXlJLFdBQ0F6SSxTQUFBMEksaUJBQUEsbUJBQUFGLEdBR0FBLG1DQ1ZBNUosT0FBQUMsZUFBQWIsRUFBQSxjQUE4Q2tCLE9BQUEsSUFLOUMsTUFBQXlKLEVBQUE3SyxFQUFBLEdBd0JBRSxFQUFBNEssdUJBdEJBUiwrQkFBQTFILEdBQ0EsTUFBQWdCLFNBQWVBLEdBQVdpSCxFQUFBdEgseUJBQUFYLEdBQzFCb0QsS0FBQStFLFFBQUFuSCxLQUFBM0IsU0FFQXFJLFFBQUFVLEdBQ0FoRixLQUFBaUYscUJBQUFqRixLQUFBa0YsVUFDQWxGLEtBQUFtRixtQkFBQUgsR0FDQWhGLEtBQUFrRixTQUFBRixFQUVBVixxQkFBQXJJLEdBQ0FBLElBR0FBLEVBQUFtSixVQUFBbkosRUFBQW1KLFVBQUFwQixRQUFBLDZCQUVBTSxtQkFBQXJJLEdBQ0FBLElBR0FBLEVBQUFtSixXQUFBLHFEQ3RCQXRLLE9BQUFDLGVBQUFiLEVBQUEsY0FBOENrQixPQUFBLElBQzlDLE1BQUFpSyxFQUFBckwsRUFBQSxJQUNBc0wsRUFBQXRMLEVBQUEsR0FDQXVMLEVBQUF2TCxFQUFBLEdBQ0E2SyxFQUFBN0ssRUFBQSxHQUNBMEMsRUFBQTFDLEVBQUEsR0FDQXdMLEVBQUF4TCxFQUFBLEdBQ0EsSUFBQXlMLEdBQUEsRUFDQSxNQUFBQyxFQUFBLElBQUFMLEVBQUFQLGlCQUNBYSxFQUFBakosRUFBQUQsY0FDQTRILEVBQUF1QixtQkFFQSxJQUFBQyxFQUFBbkosRUFBQVgsUUFBQSxjQUNBc0ksRUFBQXlCLFNBQUFELEdBQ0EsTUFBQUUsRUFBQVIsRUFBQW5CLHNCQUFBQyxHQUNBbkcsT0FBQThILFdBQUFDLFVBQUFGLEdBQ0E3SCxPQUFBZ0ksb0JBQUFELFVBQUFGLEdBQ0E3SCxPQUFBaUksT0FBQSxNQUNBQyxNQUVBZCxFQUFBYixtQkFBQSxLQUNBa0IsRUFBQTVHLHlCQUNBaUUsV0FBQSxLQUVBLEdBQUE2QyxFQUFBakcsU0FBQSxDQUNBLE1BQUEzRCxFQUFBNEksRUFBQWxGLDBCQUFBa0csRUFBQWpHLFVBQ0EzRCxJQUNBd0osR0FBQSxFQUNBWixFQUFBL0YseUJBQUE3QyxFQUFBVyxXQUdBLENBQ0EsTUFBQXlKLEdBQUFWLEVBQUEvSSxLQUNBUyxNQUFBZ0osS0FDQVosR0FBQSxFQUNBWixFQUFBL0YseUJBQUF1SCxNQUdTLEtBR1QsTUFBQUMsRUFBQSxNQUNBLE1BQUFDLEVBQUFmLEVBQUE1SSxJQUNBNkksR0FBQSxFQUNBWixFQUFBL0YseUJBQUFsQyxJQUNLLElBQ0wsT0FBQUEsRUFBQStJLEtBQ0F0SSxNQUFBVCxLQUNBK0ksRUFBQS9JLE9BQ0EySixFQUFBM0osTUFSQSxHQVlBLElBQUF3SixFQUFBWixFQUFBLEtBQ0EsTUFBQWdCLEtBQ0EsSUFBQUMsRUFBQXZLLFNBQUF3SyxxQkFBQSxPQUNBLEdBQUFELEVBQUEsQ0FDQSxJQUFBck0sRUFDQSxJQUFBQSxFQUFBLEVBQW1CQSxFQUFBcU0sRUFBQW5JLE9BQW1CbEUsSUFBQSxDQUN0QyxNQUFBdU0sRUFBQUYsRUFBQXJNLEdBQ0F1TSxFQUFBQyxVQUFBQyxTQUFBLFlBQ0FGLEVBQUFDLFVBQUFFLE9BQUEsV0FFQU4sRUFBQWxKLE1BQ0F3QyxHQUFBNkcsRUFBQTdHLEdBQ0FuQixPQUFBZ0ksRUFBQWhJLE9BQ0FvSSxNQUFBSixFQUFBSSxRQUdBaEIsRUFBQXhCLFlBQUEsa0JBQUFpQyxLQUVDLElBQ0R0SSxPQUFBMEcsaUJBQUEsY0FDQWEsR0FBQSxFQUNBVyxNQUNDLEdBQ0RsSSxPQUFBMEcsaUJBQUEsVUFBQW9DLElBQ0EsR0FBQUEsRUFBQTVLLEtBQUFvSSxTQUFBbUIsRUFBQW5CLE9BR0EsT0FBQXdDLEVBQUE1SyxLQUFBdUgsTUFDQSxxQ0FDQStCLEVBQUF1QiwrQkFBQUQsRUFBQTVLLEtBQUFRLE1BQ0EsTUFDQSxpQkFDQTBKLEVBQUFVLEVBQUE1SyxLQUFBUSxLQUFBK0ksTUFHQyxHQUNEekosU0FBQTBJLGlCQUFBLFdBQUFvQyxJQUNBLElBQUFyQixFQUFBdUIsNEJBQ0EsT0FHQSxRQUFBQyxFQUFBSCxFQUFBSSxPQUFpQ0QsRUFBTUEsSUFBQUUsV0FDdkMsU0FBQUYsRUFBQUcsUUFDQSxPQUdBLE1BQUF0SixFQUFBZ0osRUFBQU8sTUFDQTNLLEVBQUFpSSxFQUFBdkYsaUNBQUF0QixHQUNBLGlCQUFBcEIsR0FBQVMsTUFBQVQsSUFDQW1KLEVBQUF4QixZQUFBLFlBQTJDM0gsS0FBQUksS0FBQVUsTUFBQWQsT0FHM0MsTUFBQTRLLEdBQUEsd0RBQ0F0TCxTQUFBMEksaUJBQUEsUUFBQW9DLElBQ0EsSUFBQUEsRUFDQSxPQUVBLElBQUFHLEVBQUFILEVBQUFJLE9BQ0EsS0FBQUQsR0FBQSxDQUNBLEdBQUFBLEVBQUFHLFNBQUEsTUFBQUgsRUFBQUcsU0FBQUgsRUFBQU0sS0FBQSxDQUNBLEdBQUFOLEVBQUE5SyxhQUFBLFFBQUFxTCxXQUFBLEtBQ0EsT0FHQSxHQUFBRixFQUFBRyxLQUFBQyxHQUFBVCxFQUFBTSxLQUFBQyxXQUFBRSxJQUNBLE9BRUEsTUFBQUMsRUFBQVYsRUFBQTlLLGFBQUEsY0FBQThLLEVBQUE5SyxhQUFBLFFBRUEsb0JBQUE2SCxLQUFBMkQsUUFNQSxHQUxBOUIsRUFBQXhCLFlBQUEsWUFBbURrRCxLQUFBSSxJQUNuRGIsRUFBQWMsc0JBQ0FkLEVBQUFlLG1CQUtBWixJQUFBRSxjQUVDLEdBQ0RuSixPQUFBMEcsaUJBQUEsU0FBQVksRUFBQSxLQUNBLEdBQUFDLEVBQ0FBLEdBQUEsTUFFQSxDQUNBLE1BQUE3SSxFQUFBaUksRUFBQXZGLGlDQUFBcEIsT0FBQUMsU0FDQSxpQkFBQXZCLEdBQUFTLE1BQUFULEtBQ0FtSixFQUFBeEIsWUFBQSxjQUFpRDNILFNBQ2pEaUosRUFBQWpKLE9BQ0F5SCxFQUFBeUIsU0FBQUQsTUFHQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMTEpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5mdW5jdGlvbiBjbGFtcChtaW4sIG1heCwgdmFsdWUpIHtcbiAgICByZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5mdW5jdGlvbiBjbGFtcExpbmUobGluZSkge1xuICAgIHJldHVybiBjbGFtcCgwLCBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcbiAgICBsZXQgZWxlbWVudHM7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgICAgaWYgKCFlbGVtZW50cykge1xuICAgICAgICAgICAgZWxlbWVudHMgPSBbeyBlbGVtZW50OiBkb2N1bWVudC5ib2R5LCBsaW5lOiAwIH1dO1xuICAgICAgICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJyk7XG4gICAgICAgICAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudCwgbGluZSB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGVsZW1lbnRzO1xuICAgIH07XG59KSgpO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmZ1bmN0aW9uIGdldEVsZW1lbnRzRm9yU291cmNlTGluZSh0YXJnZXRMaW5lKSB7XG4gICAgY29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgbGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG4gICAgICAgIGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGVudHJ5LmxpbmUgPiBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91cywgbmV4dDogZW50cnkgfTtcbiAgICAgICAgfVxuICAgICAgICBwcmV2aW91cyA9IGVudHJ5O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91cyB9O1xufVxuZXhwb3J0cy5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmU7XG4vKipcbiAqIEZpbmQgdGhlIGh0bWwgZWxlbWVudHMgdGhhdCBhcmUgYXQgYSBzcGVjaWZpYyBwaXhlbCBvZmZzZXQgb24gdGhlIHBhZ2UuXG4gKi9cbmZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpIHtcbiAgICBjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcbiAgICBjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuICAgIGxldCBsbyA9IC0xO1xuICAgIGxldCBoaSA9IGxpbmVzLmxlbmd0aCAtIDE7XG4gICAgd2hpbGUgKGxvICsgMSA8IGhpKSB7XG4gICAgICAgIGNvbnN0IG1pZCA9IE1hdGguZmxvb3IoKGxvICsgaGkpIC8gMik7XG4gICAgICAgIGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgaWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG4gICAgICAgICAgICBoaSA9IG1pZDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxvID0gbWlkO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGhpRWxlbWVudCA9IGxpbmVzW2hpXTtcbiAgICBjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0O1xuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5mdW5jdGlvbiBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSkge1xuICAgIGlmICghc2V0dGluZ3NfMS5nZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGxpbmUgPD0gMCkge1xuICAgICAgICB3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCAwKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgaWYgKCFwcmV2aW91cykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBzY3JvbGxUbyA9IDA7XG4gICAgY29uc3QgcmVjdCA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgY29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcbiAgICBpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcbiAgICAgICAgLy8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cbiAgICAgICAgY29uc3QgYmV0d2VlblByb2dyZXNzID0gKGxpbmUgLSBwcmV2aW91cy5saW5lKSAvIChuZXh0LmxpbmUgLSBwcmV2aW91cy5saW5lKTtcbiAgICAgICAgY29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcbiAgICAgICAgc2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgKHJlY3QuaGVpZ2h0ICogcHJvZ3Jlc3NJbkVsZW1lbnQpO1xuICAgIH1cbiAgICB3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG59XG5leHBvcnRzLnNjcm9sbFRvUmV2ZWFsU291cmNlTGluZSA9IHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZTtcbmZ1bmN0aW9uIGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCkge1xuICAgIGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpO1xuICAgIGlmIChwcmV2aW91cykge1xuICAgICAgICBjb25zdCBwcmV2aW91c0JvdW5kcyA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgIGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcbiAgICAgICAgICAgIGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50ID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKHByZXZpb3VzQm91bmRzLmhlaWdodCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzV2l0aGluRWxlbWVudDtcbiAgICAgICAgICAgIHJldHVybiBjbGFtcExpbmUobGluZSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG59XG5leHBvcnRzLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0ID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQ7XG4vKipcbiAqIFRyeSB0byBmaW5kIHRoZSBodG1sIGVsZW1lbnQgYnkgdXNpbmcgYSBmcmFnbWVudCBpZFxuICovXG5mdW5jdGlvbiBnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KGZyYWdtZW50KSB7XG4gICAgcmV0dXJuIGdldENvZGVMaW5lRWxlbWVudHMoKS5maW5kKChlbGVtZW50KSA9PiB7XG4gICAgICAgIHJldHVybiBlbGVtZW50LmVsZW1lbnQuaWQgPT09IGZyYWdtZW50O1xuICAgIH0pO1xufVxuZXhwb3J0cy5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50ID0gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudDtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmV4cG9ydHMuY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZSkgPT4ge1xuICAgIHJldHVybiBuZXcgY2xhc3Mge1xuICAgICAgICBwb3N0TWVzc2FnZSh0eXBlLCBib2R5KSB7XG4gICAgICAgICAgICB2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgICAgICAgc291cmNlOiBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkuc291cmNlLFxuICAgICAgICAgICAgICAgIGJvZHlcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcbn07XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGYpIHtcbiAgICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBmKCk7XG4gICAgfVxufVxuZXhwb3J0cy5vbmNlRG9jdW1lbnRMb2FkZWQgPSBvbmNlRG9jdW1lbnRMb2FkZWQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuY29uc3Qgc2Nyb2xsX3N5bmNfMSA9IHJlcXVpcmUoXCIuL3Njcm9sbC1zeW5jXCIpO1xuY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG4gICAgb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmUpIHtcbiAgICAgICAgY29uc3QgeyBwcmV2aW91cyB9ID0gc2Nyb2xsX3N5bmNfMS5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcbiAgICB9XG4gICAgX3VwZGF0ZShiZWZvcmUpIHtcbiAgICAgICAgdGhpcy5fdW5tYXJrQWN0aXZlRWxlbWVudCh0aGlzLl9jdXJyZW50KTtcbiAgICAgICAgdGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcbiAgICAgICAgdGhpcy5fY3VycmVudCA9IGJlZm9yZTtcbiAgICB9XG4gICAgX3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSA9IGVsZW1lbnQuY2xhc3NOYW1lLnJlcGxhY2UoL1xcYmNvZGUtYWN0aXZlLWxpbmVcXGIvZywgJycpO1xuICAgIH1cbiAgICBfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuICAgIH1cbn1cbmV4cG9ydHMuQWN0aXZlTGluZU1hcmtlciA9IEFjdGl2ZUxpbmVNYXJrZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbi8vIFNldCBWUyBDb2RlIHN0YXRlXG5sZXQgc3RhdGUgPSBzZXR0aW5nc18xLmdldERhdGEoJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5jb25zdCBtZXNzYWdpbmcgPSBtZXNzYWdpbmdfMS5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuICAgIHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5ldmVudHNfMS5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuICAgIGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICBpZiAoc3RhdGUuZnJhZ21lbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHN0YXRlLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKGluaXRpYWxMaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDApO1xuICAgIH1cbn0pO1xuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcbiAgICBjb25zdCBkb1Njcm9sbCA9IHRocm90dGxlKChsaW5lKSA9PiB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgc2Nyb2xsX3N5bmNfMS5zY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG4gICAgfSwgNTApO1xuICAgIHJldHVybiAobGluZSwgc2V0dGluZ3MpID0+IHtcbiAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgc2V0dGluZ3MubGluZSA9IGxpbmU7XG4gICAgICAgICAgICBkb1Njcm9sbChsaW5lKTtcbiAgICAgICAgfVxuICAgIH07XG59KSgpO1xubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG4gICAgY29uc3QgaW1hZ2VJbmZvID0gW107XG4gICAgbGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcbiAgICBpZiAoaW1hZ2VzKSB7XG4gICAgICAgIGxldCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgaW1hZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBpbWcgPSBpbWFnZXNbaV07XG4gICAgICAgICAgICBpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG4gICAgICAgICAgICAgICAgaW1nLmNsYXNzTGlzdC5yZW1vdmUoJ2xvYWRpbmcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGltYWdlSW5mby5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogaW1nLmlkLFxuICAgICAgICAgICAgICAgIGhlaWdodDogaW1nLmhlaWdodCxcbiAgICAgICAgICAgICAgICB3aWR0aDogaW1nLndpZHRoXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG4gICAgfVxufSwgNTApO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcbiAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG59LCBmYWxzZSk7XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3NcbiAgICBmb3IgKGxldCBub2RlID0gZXZlbnQudGFyZ2V0OyBub2RlOyBub2RlID0gbm9kZS5wYXJlbnROb2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcbiAgICB9XG59KTtcbmNvbnN0IHBhc3NUaHJvdWdoTGlua1NjaGVtZXMgPSBbJ2h0dHA6JywgJ2h0dHBzOicsICdtYWlsdG86JywgJ3ZzY29kZTonLCAndnNjb2RlLWluc2lkZXJzJ107XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIWV2ZW50KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IG5vZGUgPSBldmVudC50YXJnZXQ7XG4gICAgd2hpbGUgKG5vZGUpIHtcbiAgICAgICAgaWYgKG5vZGUudGFnTmFtZSAmJiBub2RlLnRhZ05hbWUgPT09ICdBJyAmJiBub2RlLmhyZWYpIHtcbiAgICAgICAgICAgIGlmIChub2RlLmdldEF0dHJpYnV0ZSgnaHJlZicpLnN0YXJ0c1dpdGgoJyMnKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFBhc3MgdGhyb3VnaCBrbm93biBzY2hlbWVzXG4gICAgICAgICAgICBpZiAocGFzc1Rocm91Z2hMaW5rU2NoZW1lcy5zb21lKHNjaGVtZSA9PiBub2RlLmhyZWYuc3RhcnRzV2l0aChzY2hlbWUpKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGhyZWZUZXh0ID0gbm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtaHJlZicpIHx8IG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJyk7XG4gICAgICAgICAgICAvLyBJZiBvcmlnaW5hbCBsaW5rIGRvZXNuJ3QgbG9vayBsaWtlIGEgdXJsLCBkZWxlZ2F0ZSBiYWNrIHRvIFZTIENvZGUgdG8gcmVzb2x2ZVxuICAgICAgICAgICAgaWYgKCEvXlthLXpcXC1dKzovaS50ZXN0KGhyZWZUZXh0KSkge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnb3BlbkxpbmsnLCB7IGhyZWY6IGhyZWZUZXh0IH0pO1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG4gICAgfVxufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhyb3R0bGUoKCkgPT4ge1xuICAgIGlmIChzY3JvbGxEaXNhYmxlZCkge1xuICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IGZhbHNlO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgY29uc3QgbGluZSA9IHNjcm9sbF9zeW5jXzEuZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQod2luZG93LnNjcm9sbFkpO1xuICAgICAgICBpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdyZXZlYWxMaW5lJywgeyBsaW5lIH0pO1xuICAgICAgICAgICAgc3RhdGUubGluZSA9IGxpbmU7XG4gICAgICAgICAgICB2c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuICAgICAgICB9XG4gICAgfVxufSwgNTApKTtcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/extensions/markdown-language-features/media/pre.js b/extensions/markdown-language-features/media/pre.js index 5c9c7fb3b8b..28098246c05 100644 --- a/extensions/markdown-language-features/media/pre.js +++ b/extensions/markdown-language-features/media/pre.js @@ -1,280 +1,2 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./preview-src/pre.ts"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./preview-src/csp.ts": -/*!****************************!*\ - !*** ./preview-src/csp.ts ***! - \****************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const settings_1 = __webpack_require__(/*! ./settings */ "./preview-src/settings.ts"); -const strings_1 = __webpack_require__(/*! ./strings */ "./preview-src/strings.ts"); -/** - * Shows an alert when there is a content security policy violation. - */ -class CspAlerter { - constructor() { - this.didShow = false; - this.didHaveCspWarning = false; - document.addEventListener('securitypolicyviolation', () => { - this.onCspWarning(); - }); - window.addEventListener('message', (event) => { - if (event && event.data && event.data.name === 'vscode-did-block-svg') { - this.onCspWarning(); - } - }); - } - setPoster(poster) { - this.messaging = poster; - if (this.didHaveCspWarning) { - this.showCspWarning(); - } - } - onCspWarning() { - this.didHaveCspWarning = true; - this.showCspWarning(); - } - showCspWarning() { - const strings = strings_1.getStrings(); - const settings = settings_1.getSettings(); - if (this.didShow || settings.disableSecurityWarnings || !this.messaging) { - return; - } - this.didShow = true; - const notification = document.createElement('a'); - notification.innerText = strings.cspAlertMessageText; - notification.setAttribute('id', 'code-csp-warning'); - notification.setAttribute('title', strings.cspAlertMessageTitle); - notification.setAttribute('role', 'button'); - notification.setAttribute('aria-label', strings.cspAlertMessageLabel); - notification.onclick = () => { - this.messaging.postMessage('showPreviewSecuritySelector', { source: settings.source }); - }; - document.body.appendChild(notification); - } -} -exports.CspAlerter = CspAlerter; - - -/***/ }), - -/***/ "./preview-src/loading.ts": -/*!********************************!*\ - !*** ./preview-src/loading.ts ***! - \********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -class StyleLoadingMonitor { - constructor() { - this.unloadedStyles = []; - this.finishedLoading = false; - const onStyleLoadError = (event) => { - const source = event.target.dataset.source; - this.unloadedStyles.push(source); - }; - window.addEventListener('DOMContentLoaded', () => { - for (const link of document.getElementsByClassName('code-user-style')) { - if (link.dataset.source) { - link.onerror = onStyleLoadError; - } - } - }); - window.addEventListener('load', () => { - if (!this.unloadedStyles.length) { - return; - } - this.finishedLoading = true; - if (this.poster) { - this.poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); - } - }); - } - setPoster(poster) { - this.poster = poster; - if (this.finishedLoading) { - poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); - } - } -} -exports.StyleLoadingMonitor = StyleLoadingMonitor; - - -/***/ }), - -/***/ "./preview-src/pre.ts": -/*!****************************!*\ - !*** ./preview-src/pre.ts ***! - \****************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const csp_1 = __webpack_require__(/*! ./csp */ "./preview-src/csp.ts"); -const loading_1 = __webpack_require__(/*! ./loading */ "./preview-src/loading.ts"); -window.cspAlerter = new csp_1.CspAlerter(); -window.styleLoadingMonitor = new loading_1.StyleLoadingMonitor(); - - -/***/ }), - -/***/ "./preview-src/settings.ts": -/*!*********************************!*\ - !*** ./preview-src/settings.ts ***! - \*********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -let cachedSettings = undefined; -function getData(key) { - const element = document.getElementById('vscode-markdown-preview-data'); - if (element) { - const data = element.getAttribute(key); - if (data) { - return JSON.parse(data); - } - } - throw new Error(`Could not load data for ${key}`); -} -exports.getData = getData; -function getSettings() { - if (cachedSettings) { - return cachedSettings; - } - cachedSettings = getData('data-settings'); - if (cachedSettings) { - return cachedSettings; - } - throw new Error('Could not load settings'); -} -exports.getSettings = getSettings; - - -/***/ }), - -/***/ "./preview-src/strings.ts": -/*!********************************!*\ - !*** ./preview-src/strings.ts ***! - \********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -function getStrings() { - const store = document.getElementById('vscode-markdown-preview-data'); - if (store) { - const data = store.getAttribute('data-strings'); - if (data) { - return JSON.parse(data); - } - } - throw new Error('Could not load strings'); -} -exports.getStrings = getStrings; - - -/***/ }) - -/******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvY3NwLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3NldHRpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3N0cmluZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0EseURBQWlELGNBQWM7QUFDL0Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsbUNBQTJCLDBCQUEwQixFQUFFO0FBQ3ZELHlDQUFpQyxlQUFlO0FBQ2hEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDhEQUFzRCwrREFBK0Q7O0FBRXJIO0FBQ0E7OztBQUdBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7QUNuRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhDQUE4QyxjQUFjO0FBQzVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1RUFBdUUsMEJBQTBCO0FBQ2pHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7QUNyREE7QUFDQSw4Q0FBOEMsY0FBYztBQUM1RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtFQUFrRSxzQ0FBc0M7QUFDeEc7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5REFBeUQsc0NBQXNDO0FBQy9GO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7O0FDbENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEMsY0FBYztBQUM1RDtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQ1RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEMsY0FBYztBQUM1RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQ0FBK0MsSUFBSTtBQUNuRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7OztBQzVCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOENBQThDLGNBQWM7QUFDNUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJwcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9wcmUudHNcIik7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3Qgc2V0dGluZ3NfMSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzXCIpO1xuY29uc3Qgc3RyaW5nc18xID0gcmVxdWlyZShcIi4vc3RyaW5nc1wiKTtcbi8qKlxuICogU2hvd3MgYW4gYWxlcnQgd2hlbiB0aGVyZSBpcyBhIGNvbnRlbnQgc2VjdXJpdHkgcG9saWN5IHZpb2xhdGlvbi5cbiAqL1xuY2xhc3MgQ3NwQWxlcnRlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZGlkU2hvdyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmRpZEhhdmVDc3BXYXJuaW5nID0gZmFsc2U7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3NlY3VyaXR5cG9saWN5dmlvbGF0aW9uJywgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5vbkNzcFdhcm5pbmcoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBpZiAoZXZlbnQgJiYgZXZlbnQuZGF0YSAmJiBldmVudC5kYXRhLm5hbWUgPT09ICd2c2NvZGUtZGlkLWJsb2NrLXN2ZycpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9uQ3NwV2FybmluZygpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG4gICAgc2V0UG9zdGVyKHBvc3Rlcikge1xuICAgICAgICB0aGlzLm1lc3NhZ2luZyA9IHBvc3RlcjtcbiAgICAgICAgaWYgKHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcpIHtcbiAgICAgICAgICAgIHRoaXMuc2hvd0NzcFdhcm5pbmcoKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBvbkNzcFdhcm5pbmcoKSB7XG4gICAgICAgIHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG4gICAgfVxuICAgIHNob3dDc3BXYXJuaW5nKCkge1xuICAgICAgICBjb25zdCBzdHJpbmdzID0gc3RyaW5nc18xLmdldFN0cmluZ3MoKTtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBzZXR0aW5nc18xLmdldFNldHRpbmdzKCk7XG4gICAgICAgIGlmICh0aGlzLmRpZFNob3cgfHwgc2V0dGluZ3MuZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MgfHwgIXRoaXMubWVzc2FnaW5nKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5kaWRTaG93ID0gdHJ1ZTtcbiAgICAgICAgY29uc3Qgbm90aWZpY2F0aW9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICAgICAgICBub3RpZmljYXRpb24uaW5uZXJUZXh0ID0gc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUZXh0O1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdpZCcsICdjb2RlLWNzcC13YXJuaW5nJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3RpdGxlJywgc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUaXRsZSk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCBzdHJpbmdzLmNzcEFsZXJ0TWVzc2FnZUxhYmVsKTtcbiAgICAgICAgbm90aWZpY2F0aW9uLm9uY2xpY2sgPSAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnc2hvd1ByZXZpZXdTZWN1cml0eVNlbGVjdG9yJywgeyBzb3VyY2U6IHNldHRpbmdzLnNvdXJjZSB9KTtcbiAgICAgICAgfTtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChub3RpZmljYXRpb24pO1xuICAgIH1cbn1cbmV4cG9ydHMuQ3NwQWxlcnRlciA9IENzcEFsZXJ0ZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNsYXNzIFN0eWxlTG9hZGluZ01vbml0b3Ige1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnVubG9hZGVkU3R5bGVzID0gW107XG4gICAgICAgIHRoaXMuZmluaXNoZWRMb2FkaW5nID0gZmFsc2U7XG4gICAgICAgIGNvbnN0IG9uU3R5bGVMb2FkRXJyb3IgPSAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHNvdXJjZSA9IGV2ZW50LnRhcmdldC5kYXRhc2V0LnNvdXJjZTtcbiAgICAgICAgICAgIHRoaXMudW5sb2FkZWRTdHlsZXMucHVzaChzb3VyY2UpO1xuICAgICAgICB9O1xuICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsICgpID0+IHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgbGluayBvZiBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdjb2RlLXVzZXItc3R5bGUnKSkge1xuICAgICAgICAgICAgICAgIGlmIChsaW5rLmRhdGFzZXQuc291cmNlKSB7XG4gICAgICAgICAgICAgICAgICAgIGxpbmsub25lcnJvciA9IG9uU3R5bGVMb2FkRXJyb3I7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ2xvYWQnLCAoKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMudW5sb2FkZWRTdHlsZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5maW5pc2hlZExvYWRpbmcgPSB0cnVlO1xuICAgICAgICAgICAgaWYgKHRoaXMucG9zdGVyKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5wb3N0ZXIucG9zdE1lc3NhZ2UoJ3ByZXZpZXdTdHlsZUxvYWRFcnJvcicsIHsgdW5sb2FkZWRTdHlsZXM6IHRoaXMudW5sb2FkZWRTdHlsZXMgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICBzZXRQb3N0ZXIocG9zdGVyKSB7XG4gICAgICAgIHRoaXMucG9zdGVyID0gcG9zdGVyO1xuICAgICAgICBpZiAodGhpcy5maW5pc2hlZExvYWRpbmcpIHtcbiAgICAgICAgICAgIHBvc3Rlci5wb3N0TWVzc2FnZSgncHJldmlld1N0eWxlTG9hZEVycm9yJywgeyB1bmxvYWRlZFN0eWxlczogdGhpcy51bmxvYWRlZFN0eWxlcyB9KTtcbiAgICAgICAgfVxuICAgIH1cbn1cbmV4cG9ydHMuU3R5bGVMb2FkaW5nTW9uaXRvciA9IFN0eWxlTG9hZGluZ01vbml0b3I7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgY3NwXzEgPSByZXF1aXJlKFwiLi9jc3BcIik7XG5jb25zdCBsb2FkaW5nXzEgPSByZXF1aXJlKFwiLi9sb2FkaW5nXCIpO1xud2luZG93LmNzcEFsZXJ0ZXIgPSBuZXcgY3NwXzEuQ3NwQWxlcnRlcigpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3IgPSBuZXcgbG9hZGluZ18xLlN0eWxlTG9hZGluZ01vbml0b3IoKTtcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5sZXQgY2FjaGVkU2V0dGluZ3MgPSB1bmRlZmluZWQ7XG5mdW5jdGlvbiBnZXREYXRhKGtleSkge1xuICAgIGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuICAgIGlmIChlbGVtZW50KSB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgbG9hZCBkYXRhIGZvciAke2tleX1gKTtcbn1cbmV4cG9ydHMuZ2V0RGF0YSA9IGdldERhdGE7XG5mdW5jdGlvbiBnZXRTZXR0aW5ncygpIHtcbiAgICBpZiAoY2FjaGVkU2V0dGluZ3MpIHtcbiAgICAgICAgcmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuICAgIH1cbiAgICBjYWNoZWRTZXR0aW5ncyA9IGdldERhdGEoJ2RhdGEtc2V0dGluZ3MnKTtcbiAgICBpZiAoY2FjaGVkU2V0dGluZ3MpIHtcbiAgICAgICAgcmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBsb2FkIHNldHRpbmdzJyk7XG59XG5leHBvcnRzLmdldFNldHRpbmdzID0gZ2V0U2V0dGluZ3M7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gZ2V0U3RyaW5ncygpIHtcbiAgICBjb25zdCBzdG9yZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKHN0b3JlKSB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBzdG9yZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RyaW5ncycpO1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzdHJpbmdzJyk7XG59XG5leHBvcnRzLmdldFN0cmluZ3MgPSBnZXRTdHJpbmdzO1xuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file +!function(e){var t={};function s(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,s),o.l=!0,o.exports}s.m=e,s.c=t,s.d=function(e,t,n){s.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},s.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=5)}([function(e,t,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=void 0;function o(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const s=t.getAttribute(e);if(s)return JSON.parse(s)}throw new Error(`Could not load data for ${e}`)}t.getData=o,t.getSettings=function(){if(n)return n;if(n=o("data-settings"))return n;throw new Error("Could not load settings")}},,function(e,t,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.StyleLoadingMonitor=class{constructor(){this.unloadedStyles=[],this.finishedLoading=!1;const e=e=>{const t=e.target.dataset.source;this.unloadedStyles.push(t)};window.addEventListener("DOMContentLoaded",()=>{for(const t of document.getElementsByClassName("code-user-style"))t.dataset.source&&(t.onerror=e)}),window.addEventListener("load",()=>{this.unloadedStyles.length&&(this.finishedLoading=!0,this.poster&&this.poster.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles}))})}setPoster(e){this.poster=e,this.finishedLoading&&e.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles})}}},function(e,t,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getStrings=function(){const e=document.getElementById("vscode-markdown-preview-data");if(e){const t=e.getAttribute("data-strings");if(t)return JSON.parse(t)}throw new Error("Could not load strings")}},function(e,t,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=s(0),o=s(3);t.CspAlerter=class{constructor(){this.didShow=!1,this.didHaveCspWarning=!1,document.addEventListener("securitypolicyviolation",()=>{this.onCspWarning()}),window.addEventListener("message",e=>{e&&e.data&&"vscode-did-block-svg"===e.data.name&&this.onCspWarning()})}setPoster(e){this.messaging=e,this.didHaveCspWarning&&this.showCspWarning()}onCspWarning(){this.didHaveCspWarning=!0,this.showCspWarning()}showCspWarning(){const e=o.getStrings(),t=n.getSettings();if(this.didShow||t.disableSecurityWarnings||!this.messaging)return;this.didShow=!0;const s=document.createElement("a");s.innerText=e.cspAlertMessageText,s.setAttribute("id","code-csp-warning"),s.setAttribute("title",e.cspAlertMessageTitle),s.setAttribute("role","button"),s.setAttribute("aria-label",e.cspAlertMessageLabel),s.onclick=(()=>{this.messaging.postMessage("showPreviewSecuritySelector",{source:t.source})}),document.body.appendChild(s)}}},function(e,t,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=s(4),o=s(2);window.cspAlerter=new n.CspAlerter,window.styleLoadingMonitor=new o.StyleLoadingMonitor}]); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvbG9hZGluZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zdHJpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2NzcC50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9wcmUudHMiXSwibmFtZXMiOlsiaW5zdGFsbGVkTW9kdWxlcyIsIl9fd2VicGFja19yZXF1aXJlX18iLCJtb2R1bGVJZCIsImV4cG9ydHMiLCJtb2R1bGUiLCJpIiwibCIsIm1vZHVsZXMiLCJjYWxsIiwibSIsImMiLCJkIiwibmFtZSIsImdldHRlciIsIm8iLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImNvbmZpZ3VyYWJsZSIsImVudW1lcmFibGUiLCJnZXQiLCJyIiwidmFsdWUiLCJuIiwiX19lc01vZHVsZSIsIm9iamVjdCIsInByb3BlcnR5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJwIiwicyIsImNhY2hlZFNldHRpbmdzIiwidW5kZWZpbmVkIiwiZ2V0RGF0YSIsImtleSIsImVsZW1lbnQiLCJkb2N1bWVudCIsImdldEVsZW1lbnRCeUlkIiwiZGF0YSIsImdldEF0dHJpYnV0ZSIsIkpTT04iLCJwYXJzZSIsIkVycm9yIiwiZ2V0U2V0dGluZ3MiLCJTdHlsZUxvYWRpbmdNb25pdG9yIiwiW29iamVjdCBPYmplY3RdIiwidGhpcyIsInVubG9hZGVkU3R5bGVzIiwiZmluaXNoZWRMb2FkaW5nIiwib25TdHlsZUxvYWRFcnJvciIsImV2ZW50Iiwic291cmNlIiwidGFyZ2V0IiwiZGF0YXNldCIsInB1c2giLCJ3aW5kb3ciLCJhZGRFdmVudExpc3RlbmVyIiwibGluayIsImdldEVsZW1lbnRzQnlDbGFzc05hbWUiLCJvbmVycm9yIiwibGVuZ3RoIiwicG9zdGVyIiwicG9zdE1lc3NhZ2UiLCJnZXRTdHJpbmdzIiwic3RvcmUiLCJzZXR0aW5nc18xIiwic3RyaW5nc18xIiwiQ3NwQWxlcnRlciIsImRpZFNob3ciLCJkaWRIYXZlQ3NwV2FybmluZyIsIm9uQ3NwV2FybmluZyIsIm1lc3NhZ2luZyIsInNob3dDc3BXYXJuaW5nIiwic3RyaW5ncyIsInNldHRpbmdzIiwiZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MiLCJub3RpZmljYXRpb24iLCJjcmVhdGVFbGVtZW50IiwiaW5uZXJUZXh0IiwiY3NwQWxlcnRNZXNzYWdlVGV4dCIsInNldEF0dHJpYnV0ZSIsImNzcEFsZXJ0TWVzc2FnZVRpdGxlIiwiY3NwQWxlcnRNZXNzYWdlTGFiZWwiLCJvbmNsaWNrIiwiYm9keSIsImFwcGVuZENoaWxkIiwiY3NwXzEiLCJsb2FkaW5nXzEiLCJjc3BBbGVydGVyIiwic3R5bGVMb2FkaW5nTW9uaXRvciJdLCJtYXBwaW5ncyI6ImFBQ0EsSUFBQUEsS0FHQSxTQUFBQyxFQUFBQyxHQUdBLEdBQUFGLEVBQUFFLEdBQ0EsT0FBQUYsRUFBQUUsR0FBQUMsUUFHQSxJQUFBQyxFQUFBSixFQUFBRSxJQUNBRyxFQUFBSCxFQUNBSSxHQUFBLEVBQ0FILFlBVUEsT0FOQUksRUFBQUwsR0FBQU0sS0FBQUosRUFBQUQsUUFBQUMsSUFBQUQsUUFBQUYsR0FHQUcsRUFBQUUsR0FBQSxFQUdBRixFQUFBRCxRQUtBRixFQUFBUSxFQUFBRixFQUdBTixFQUFBUyxFQUFBVixFQUdBQyxFQUFBVSxFQUFBLFNBQUFSLEVBQUFTLEVBQUFDLEdBQ0FaLEVBQUFhLEVBQUFYLEVBQUFTLElBQ0FHLE9BQUFDLGVBQUFiLEVBQUFTLEdBQ0FLLGNBQUEsRUFDQUMsWUFBQSxFQUNBQyxJQUFBTixLQU1BWixFQUFBbUIsRUFBQSxTQUFBakIsR0FDQVksT0FBQUMsZUFBQWIsRUFBQSxjQUFpRGtCLE9BQUEsS0FJakRwQixFQUFBcUIsRUFBQSxTQUFBbEIsR0FDQSxJQUFBUyxFQUFBVCxLQUFBbUIsV0FDQSxXQUEyQixPQUFBbkIsRUFBQSxTQUMzQixXQUFpQyxPQUFBQSxHQUVqQyxPQURBSCxFQUFBVSxFQUFBRSxFQUFBLElBQUFBLEdBQ0FBLEdBSUFaLEVBQUFhLEVBQUEsU0FBQVUsRUFBQUMsR0FBc0QsT0FBQVYsT0FBQVcsVUFBQUMsZUFBQW5CLEtBQUFnQixFQUFBQyxJQUd0RHhCLEVBQUEyQixFQUFBLEdBSUEzQixJQUFBNEIsRUFBQSxrQ0M5REFkLE9BQUFDLGVBQUFiLEVBQUEsY0FBOENrQixPQUFBLElBQzlDLElBQUFTLE9BQUFDLEVBQ0EsU0FBQUMsRUFBQUMsR0FDQSxNQUFBQyxFQUFBQyxTQUFBQyxlQUFBLGdDQUNBLEdBQUFGLEVBQUEsQ0FDQSxNQUFBRyxFQUFBSCxFQUFBSSxhQUFBTCxHQUNBLEdBQUFJLEVBQ0EsT0FBQUUsS0FBQUMsTUFBQUgsR0FHQSxVQUFBSSxpQ0FBK0NSLEtBRS9DOUIsRUFBQTZCLFVBV0E3QixFQUFBdUMsWUFWQSxXQUNBLEdBQUFaLEVBQ0EsT0FBQUEsRUFHQSxHQURBQSxFQUFBRSxFQUFBLGlCQUVBLE9BQUFGLEVBRUEsVUFBQVcsTUFBQSwyREN6QkExQixPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQWlDOUNsQixFQUFBd0MsMEJBL0JBQyxjQUNBQyxLQUFBQyxrQkFDQUQsS0FBQUUsaUJBQUEsRUFDQSxNQUFBQyxFQUFBQyxJQUNBLE1BQUFDLEVBQUFELEVBQUFFLE9BQUFDLFFBQUFGLE9BQ0FMLEtBQUFDLGVBQUFPLEtBQUFILElBRUFJLE9BQUFDLGlCQUFBLHdCQUNBLFVBQUFDLEtBQUFyQixTQUFBc0IsdUJBQUEsbUJBQ0FELEVBQUFKLFFBQUFGLFNBQ0FNLEVBQUFFLFFBQUFWLEtBSUFNLE9BQUFDLGlCQUFBLFlBQ0FWLEtBQUFDLGVBQUFhLFNBR0FkLEtBQUFFLGlCQUFBLEVBQ0FGLEtBQUFlLFFBQ0FmLEtBQUFlLE9BQUFDLFlBQUEseUJBQWtFZixlQUFBRCxLQUFBQyxvQkFJbEVGLFVBQUFnQixHQUNBZixLQUFBZSxTQUNBZixLQUFBRSxpQkFDQWEsRUFBQUMsWUFBQSx5QkFBeURmLGVBQUFELEtBQUFDLGlEQ3pCekQvQixPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQVc5Q2xCLEVBQUEyRCxXQVZBLFdBQ0EsTUFBQUMsRUFBQTVCLFNBQUFDLGVBQUEsZ0NBQ0EsR0FBQTJCLEVBQUEsQ0FDQSxNQUFBMUIsRUFBQTBCLEVBQUF6QixhQUFBLGdCQUNBLEdBQUFELEVBQ0EsT0FBQUUsS0FBQUMsTUFBQUgsR0FHQSxVQUFBSSxNQUFBLHlEQ1RBMUIsT0FBQUMsZUFBQWIsRUFBQSxjQUE4Q2tCLE9BQUEsSUFDOUMsTUFBQTJDLEVBQUEvRCxFQUFBLEdBQ0FnRSxFQUFBaEUsRUFBQSxHQThDQUUsRUFBQStELGlCQXpDQXRCLGNBQ0FDLEtBQUFzQixTQUFBLEVBQ0F0QixLQUFBdUIsbUJBQUEsRUFDQWpDLFNBQUFvQixpQkFBQSwrQkFDQVYsS0FBQXdCLGlCQUVBZixPQUFBQyxpQkFBQSxVQUFBTixJQUNBQSxLQUFBWixNQUFBLHlCQUFBWSxFQUFBWixLQUFBekIsTUFDQWlDLEtBQUF3QixpQkFJQXpCLFVBQUFnQixHQUNBZixLQUFBeUIsVUFBQVYsRUFDQWYsS0FBQXVCLG1CQUNBdkIsS0FBQTBCLGlCQUdBM0IsZUFDQUMsS0FBQXVCLG1CQUFBLEVBQ0F2QixLQUFBMEIsaUJBRUEzQixpQkFDQSxNQUFBNEIsRUFBQVAsRUFBQUgsYUFDQVcsRUFBQVQsRUFBQXRCLGNBQ0EsR0FBQUcsS0FBQXNCLFNBQUFNLEVBQUFDLDBCQUFBN0IsS0FBQXlCLFVBQ0EsT0FFQXpCLEtBQUFzQixTQUFBLEVBQ0EsTUFBQVEsRUFBQXhDLFNBQUF5QyxjQUFBLEtBQ0FELEVBQUFFLFVBQUFMLEVBQUFNLG9CQUNBSCxFQUFBSSxhQUFBLHlCQUNBSixFQUFBSSxhQUFBLFFBQUFQLEVBQUFRLHNCQUNBTCxFQUFBSSxhQUFBLGlCQUNBSixFQUFBSSxhQUFBLGFBQUFQLEVBQUFTLHNCQUNBTixFQUFBTyxRQUFBLE1BQ0FyQyxLQUFBeUIsVUFBQVQsWUFBQSwrQkFBdUVYLE9BQUF1QixFQUFBdkIsV0FFdkVmLFNBQUFnRCxLQUFBQyxZQUFBVCxtQ0M3Q0E1RCxPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQUM5QyxNQUFBZ0UsRUFBQXBGLEVBQUEsR0FDQXFGLEVBQUFyRixFQUFBLEdBQ0FxRCxPQUFBaUMsV0FBQSxJQUFBRixFQUFBbkIsV0FDQVosT0FBQWtDLG9CQUFBLElBQUFGLEVBQUEzQyIsImZpbGUiOiJwcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IDUpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY2xhc3MgU3R5bGVMb2FkaW5nTW9uaXRvciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMudW5sb2FkZWRTdHlsZXMgPSBbXTtcbiAgICAgICAgdGhpcy5maW5pc2hlZExvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgY29uc3Qgb25TdHlsZUxvYWRFcnJvciA9IChldmVudCkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc291cmNlID0gZXZlbnQudGFyZ2V0LmRhdGFzZXQuc291cmNlO1xuICAgICAgICAgICAgdGhpcy51bmxvYWRlZFN0eWxlcy5wdXNoKHNvdXJjZSk7XG4gICAgICAgIH07XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4ge1xuICAgICAgICAgICAgZm9yIChjb25zdCBsaW5rIG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtdXNlci1zdHlsZScpKSB7XG4gICAgICAgICAgICAgICAgaWYgKGxpbmsuZGF0YXNldC5zb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluay5vbmVycm9yID0gb25TdHlsZUxvYWRFcnJvcjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsICgpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy51bmxvYWRlZFN0eWxlcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmZpbmlzaGVkTG9hZGluZyA9IHRydWU7XG4gICAgICAgICAgICBpZiAodGhpcy5wb3N0ZXIpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnBvc3Rlci5wb3N0TWVzc2FnZSgncHJldmlld1N0eWxlTG9hZEVycm9yJywgeyB1bmxvYWRlZFN0eWxlczogdGhpcy51bmxvYWRlZFN0eWxlcyB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHNldFBvc3Rlcihwb3N0ZXIpIHtcbiAgICAgICAgdGhpcy5wb3N0ZXIgPSBwb3N0ZXI7XG4gICAgICAgIGlmICh0aGlzLmZpbmlzaGVkTG9hZGluZykge1xuICAgICAgICAgICAgcG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuICAgICAgICB9XG4gICAgfVxufVxuZXhwb3J0cy5TdHlsZUxvYWRpbmdNb25pdG9yID0gU3R5bGVMb2FkaW5nTW9uaXRvcjtcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5mdW5jdGlvbiBnZXRTdHJpbmdzKCkge1xuICAgIGNvbnN0IHN0b3JlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcbiAgICBpZiAoc3RvcmUpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IHN0b3JlLmdldEF0dHJpYnV0ZSgnZGF0YS1zdHJpbmdzJyk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBsb2FkIHN0cmluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U3RyaW5ncyA9IGdldFN0cmluZ3M7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3Qgc2V0dGluZ3NfMSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzXCIpO1xuY29uc3Qgc3RyaW5nc18xID0gcmVxdWlyZShcIi4vc3RyaW5nc1wiKTtcbi8qKlxuICogU2hvd3MgYW4gYWxlcnQgd2hlbiB0aGVyZSBpcyBhIGNvbnRlbnQgc2VjdXJpdHkgcG9saWN5IHZpb2xhdGlvbi5cbiAqL1xuY2xhc3MgQ3NwQWxlcnRlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZGlkU2hvdyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmRpZEhhdmVDc3BXYXJuaW5nID0gZmFsc2U7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3NlY3VyaXR5cG9saWN5dmlvbGF0aW9uJywgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5vbkNzcFdhcm5pbmcoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBpZiAoZXZlbnQgJiYgZXZlbnQuZGF0YSAmJiBldmVudC5kYXRhLm5hbWUgPT09ICd2c2NvZGUtZGlkLWJsb2NrLXN2ZycpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9uQ3NwV2FybmluZygpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG4gICAgc2V0UG9zdGVyKHBvc3Rlcikge1xuICAgICAgICB0aGlzLm1lc3NhZ2luZyA9IHBvc3RlcjtcbiAgICAgICAgaWYgKHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcpIHtcbiAgICAgICAgICAgIHRoaXMuc2hvd0NzcFdhcm5pbmcoKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBvbkNzcFdhcm5pbmcoKSB7XG4gICAgICAgIHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG4gICAgfVxuICAgIHNob3dDc3BXYXJuaW5nKCkge1xuICAgICAgICBjb25zdCBzdHJpbmdzID0gc3RyaW5nc18xLmdldFN0cmluZ3MoKTtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBzZXR0aW5nc18xLmdldFNldHRpbmdzKCk7XG4gICAgICAgIGlmICh0aGlzLmRpZFNob3cgfHwgc2V0dGluZ3MuZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MgfHwgIXRoaXMubWVzc2FnaW5nKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5kaWRTaG93ID0gdHJ1ZTtcbiAgICAgICAgY29uc3Qgbm90aWZpY2F0aW9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICAgICAgICBub3RpZmljYXRpb24uaW5uZXJUZXh0ID0gc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUZXh0O1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdpZCcsICdjb2RlLWNzcC13YXJuaW5nJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3RpdGxlJywgc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUaXRsZSk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCBzdHJpbmdzLmNzcEFsZXJ0TWVzc2FnZUxhYmVsKTtcbiAgICAgICAgbm90aWZpY2F0aW9uLm9uY2xpY2sgPSAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnc2hvd1ByZXZpZXdTZWN1cml0eVNlbGVjdG9yJywgeyBzb3VyY2U6IHNldHRpbmdzLnNvdXJjZSB9KTtcbiAgICAgICAgfTtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChub3RpZmljYXRpb24pO1xuICAgIH1cbn1cbmV4cG9ydHMuQ3NwQWxlcnRlciA9IENzcEFsZXJ0ZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgY3NwXzEgPSByZXF1aXJlKFwiLi9jc3BcIik7XG5jb25zdCBsb2FkaW5nXzEgPSByZXF1aXJlKFwiLi9sb2FkaW5nXCIpO1xud2luZG93LmNzcEFsZXJ0ZXIgPSBuZXcgY3NwXzEuQ3NwQWxlcnRlcigpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3IgPSBuZXcgbG9hZGluZ18xLlN0eWxlTG9hZGluZ01vbml0b3IoKTtcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 4cd76c86007..482d0e6133d 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -314,7 +314,7 @@ "watch": "npm run build-preview && gulp watch-extension:markdown-language-features", "vscode:prepublish": "npm run build-ext && npm run build-preview", "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json", - "build-preview": "webpack --mode development" + "build-preview": "webpack --mode production" }, "dependencies": { "highlight.js": "9.15.8", diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index b12614fe9ec..1dbde3ed4e5 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -19,7 +19,7 @@ const settings = getSettings(); const vscode = acquireVsCodeApi(); // Set VS Code state -let state = getData<{ line: number, fragment: string }>('data-state'); +let state = getData<{ line: number; fragment: string; }>('data-state'); vscode.setState(state); const messaging = createPosterForVsCode(vscode); @@ -67,7 +67,7 @@ const onUpdateView = (() => { })(); let updateImageSizes = throttle(() => { - const imageInfo: { id: string, height: number, width: number }[] = []; + const imageInfo: { id: string, height: number, width: number; }[] = []; let images = document.getElementsByTagName('img'); if (images) { let i; @@ -129,6 +129,8 @@ document.addEventListener('dblclick', event => { } }); +const passThroughLinkSchemes = ['http:', 'https:', 'mailto:', 'vscode:', 'vscode-insiders']; + document.addEventListener('click', event => { if (!event) { return; @@ -138,36 +140,40 @@ document.addEventListener('click', event => { while (node) { if (node.tagName && node.tagName === 'A' && node.href) { if (node.getAttribute('href').startsWith('#')) { - break; + return; } - if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:') || node.href.startsWith(settings.webviewResourceRoot)) { - const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').replace(new RegExp(`^${escapeRegExp(settings.webviewResourceRoot)}`)).split('#'); - messaging.postMessage('clickLink', { path, fragment }); + + // Pass through known schemes + if (passThroughLinkSchemes.some(scheme => node.href.startsWith(scheme))) { + return; + } + + const hrefText = node.getAttribute('data-href') || node.getAttribute('href'); + + // If original link doesn't look like a url, delegate back to VS Code to resolve + if (!/^[a-z\-]+:/i.test(hrefText)) { + messaging.postMessage('openLink', { href: hrefText }); event.preventDefault(); event.stopPropagation(); - break; + return; } - break; + + return; } node = node.parentNode; } }, true); -if (settings.scrollEditorWithPreview) { - window.addEventListener('scroll', throttle(() => { - if (scrollDisabled) { - scrollDisabled = false; - } else { - const line = getEditorLineNumberForPageOffset(window.scrollY); - if (typeof line === 'number' && !isNaN(line)) { - messaging.postMessage('revealLine', { line }); - state.line = line; - vscode.setState(state); - } +window.addEventListener('scroll', throttle(() => { + if (scrollDisabled) { + scrollDisabled = false; + } else { + const line = getEditorLineNumberForPageOffset(window.scrollY); + if (typeof line === 'number' && !isNaN(line)) { + messaging.postMessage('revealLine', { line }); + state.line = line; + vscode.setState(state); } - }, 50)); -} + } +}, 50)); -function escapeRegExp(text: string) { - return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); -} diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 00224e1364d..16636dd1585 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -25,7 +25,7 @@ interface WebviewMessage { interface CacheImageSizesMessage extends WebviewMessage { readonly type: 'cacheImageSizes'; - readonly body: { id: string, width: number, height: number }[]; + readonly body: { id: string, width: number, height: number; }[]; } interface RevealLineMessage extends WebviewMessage { @@ -43,10 +43,9 @@ interface DidClickMessage extends WebviewMessage { } interface ClickLinkMessage extends WebviewMessage { - readonly type: 'clickLink'; + readonly type: 'openLink'; readonly body: { - readonly path: string; - readonly fragment?: string; + readonly href: string; }; } @@ -88,7 +87,7 @@ export class MarkdownPreview extends Disposable { private forceUpdate = false; private isScrolling = false; private _disposed: boolean = false; - private imageInfo: { id: string, width: number, height: number }[] = []; + private imageInfo: { id: string, width: number, height: number; }[] = []; private scrollToFragment: string | undefined; public static async revive( @@ -202,8 +201,8 @@ export class MarkdownPreview extends Disposable { this.onDidClickPreview(e.body.line); break; - case 'clickLink': - this.onDidClickPreviewLink(e.body.path, e.body.fragment); + case 'openLink': + this.onDidClickPreviewLink(e.body.href); break; case 'showPreviewSecuritySelector': @@ -284,15 +283,17 @@ export class MarkdownPreview extends Disposable { super.dispose(); } - public update(resource: vscode.Uri) { - const editor = vscode.window.activeTextEditor; + public update(resource: vscode.Uri, isRefresh = true) { // Reposition scroll preview, position scroll to the top if active text editor // doesn't corresponds with preview + const editor = vscode.window.activeTextEditor; if (editor) { - if (editor.document.uri.fsPath === resource.fsPath) { - this.line = getVisibleLine(editor); - } else { - this.line = 0; + if (!isRefresh || this._previewConfigurations.loadAndCacheConfiguration(this._resource).scrollEditorWithPreview) { + if (editor.document.uri.fsPath === resource.fsPath) { + this.line = getVisibleLine(editor); + } else { + this.line = 0; + } } } @@ -320,7 +321,7 @@ export class MarkdownPreview extends Disposable { public refresh() { this.forceUpdate = true; - this.update(this._resource); + this.update(this._resource, true); } public updateConfiguration() { @@ -484,6 +485,12 @@ export class MarkdownPreview extends Disposable { private onDidScrollPreview(line: number) { this.line = line; + + const config = this._previewConfigurations.loadAndCacheConfiguration(this._resource); + if (!config.scrollEditorWithPreview) { + return; + } + for (const editor of vscode.window.visibleTextEditors) { if (!this.isPreviewOf(editor.document.uri)) { continue; @@ -528,12 +535,19 @@ export class MarkdownPreview extends Disposable { this.editor.webview.html = html; } - private async onDidClickPreviewLink(path: string, fragment: string | undefined) { - this.scrollToFragment = undefined; + private async onDidClickPreviewLink(href: string) { + let [hrefPath, fragment] = decodeURIComponent(href).split('#'); + + // We perviously already resolve absolute paths. + // Now make sure we handle relative file paths + if (hrefPath[0] !== '/') { + hrefPath = path.join(path.dirname(this.resource.path), hrefPath); + } + const config = vscode.workspace.getConfiguration('markdown', this.resource); const openLinks = config.get('preview.openMarkdownLinks', 'inPreview'); if (openLinks === 'inPreview') { - const markdownLink = await resolveLinkToMarkdownFile(path); + const markdownLink = await resolveLinkToMarkdownFile(hrefPath); if (markdownLink) { if (fragment) { this.scrollToFragment = fragment; @@ -543,10 +557,10 @@ export class MarkdownPreview extends Disposable { } } - vscode.commands.executeCommand('_markdown.openDocumentLink', { path, fragment, fromResource: this.resource }); + vscode.commands.executeCommand('_markdown.openDocumentLink', { path: hrefPath, fragment, fromResource: this.resource }); } - private async onCacheImageSizes(imageInfo: { id: string, width: number, height: number }[]) { + private async onCacheImageSizes(imageInfo: { id: string, width: number, height: number; }[]) { this.imageInfo = imageInfo; } } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index bcd7dc43ff4..9676278f3ab 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as crypto from 'crypto'; -import { MarkdownIt, Token } from 'markdown-it'; import * as path from 'path'; +import { MarkdownIt, Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions'; import { Slugifier } from './slugify'; import { SkinnyTextDocument } from './tableOfContentsProvider'; -import { getUriForLinkWithKnownExternalScheme } from './util/links'; +import { Schemes, isOfScheme } from './util/links'; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; @@ -105,10 +105,10 @@ export class MarkdownEngine { this.addImageStabilizer(md); this.addFencedRenderer(md); - this.addLinkNormalizer(md); this.addLinkValidator(md); this.addNamedHeaders(md); + this.addLinkRenderer(md); return md; }); } @@ -143,6 +143,7 @@ export class MarkdownEngine { public async render(input: SkinnyTextDocument | string): Promise { const config = this.getConfig(typeof input === 'string' ? undefined : input.uri); const engine = await this.getEngine(config); + const tokens = typeof input === 'string' ? this.tokenizeString(input, engine) : this.tokenizeDocument(input, config, engine); @@ -226,36 +227,28 @@ export class MarkdownEngine { const normalizeLink = md.normalizeLink; md.normalizeLink = (link: string) => { try { - const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link); - if (externalSchemeUri) { - // set true to skip encoding - return normalizeLink(externalSchemeUri.toString(true)); - } + // If original link doesn't look like a url with a scheme, assume it must be a link to a file in workspace + if (!/^[a-z\-]+:/i.test(link)) { + // Use a fake scheme for parsing + let uri = vscode.Uri.parse('markdown-link:' + link); - // Assume it must be an relative or absolute file path - // Use a fake scheme to avoid parse warnings - let uri = vscode.Uri.parse(`vscode-resource:${link}`); - - if (uri.path) { - // Assume it must be a file - const fragment = uri.fragment; + // Relative paths should be resolved correctly inside the preview but we need to + // handle absolute paths specially (for images) to resolve them relative to the workspace root if (uri.path[0] === '/') { const root = vscode.workspace.getWorkspaceFolder(this.currentDocument!); if (root) { - uri = vscode.Uri.file(path.join(root.uri.fsPath, uri.path)); + uri = uri.with({ + path: path.join(root.uri.fsPath, uri.path), + }); } - } else { - uri = vscode.Uri.file(path.join(path.dirname(this.currentDocument!.path), uri.path)); } - if (fragment) { + if (uri.fragment) { uri = uri.with({ - fragment: this.slugifier.fromHeading(fragment).value + fragment: this.slugifier.fromHeading(uri.fragment).value }); } - return normalizeLink(uri.with({ scheme: 'vscode-resource' }).toString(true)); - } else if (!uri.path && uri.fragment) { - return `#${this.slugifier.fromHeading(uri.fragment).value}`; + return normalizeLink(uri.toString(true).replace(/^markdown-link:/, '')); } } catch (e) { // noop @@ -268,7 +261,7 @@ export class MarkdownEngine { const validateLink = md.validateLink; md.validateLink = (link: string) => { // support file:// links - return validateLink(link) || link.startsWith('file:') || /^data:image\/.*?;/.test(link); + return validateLink(link) || isOfScheme(Schemes.file, link) || /^data:image\/.*?;/.test(link); }; } @@ -296,6 +289,22 @@ export class MarkdownEngine { } }; } + + private addLinkRenderer(md: any): void { + const old_render = md.renderer.rules.link_open || ((tokens: any, idx: number, options: any, _env: any, self: any) => { + return self.renderToken(tokens, idx, options); + }); + + md.renderer.rules.link_open = (tokens: any, idx: number, options: any, env: any, self: any) => { + const token = tokens[idx]; + const hrefIndex = token.attrIndex('href'); + if (hrefIndex >= 0) { + const href = token.attrs[hrefIndex][1]; + token.attrPush(['data-href', href]); + } + return old_render(tokens, idx, options, env, self); + }; + } } async function getMarkdownOptions(md: () => MarkdownIt) { @@ -303,16 +312,7 @@ async function getMarkdownOptions(md: () => MarkdownIt) { return { html: true, highlight: (str: string, lang?: string) => { - // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 - if (lang && ['tsx', 'typescriptreact'].includes(lang.toLocaleLowerCase())) { - lang = 'jsx'; - } - if (lang && lang.toLocaleLowerCase() === 'json5') { - lang = 'json'; - } - if (lang && ['c#', 'csharp'].includes(lang.toLocaleLowerCase())) { - lang = 'cs'; - } + lang = normalizeHighlightLang(lang); if (lang && hljs.getLanguage(lang)) { try { return `
${hljs.highlight(lang, str, true).value}
`; @@ -323,3 +323,24 @@ async function getMarkdownOptions(md: () => MarkdownIt) { } }; } + +function normalizeHighlightLang(lang: string | undefined) { + switch (lang && lang.toLowerCase()) { + case 'tsx': + case 'typescriptreact': + // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 + return 'jsx'; + + case 'json5': + case 'jsonc': + return 'json'; + + case 'c#': + case 'csharp': + return 'cs'; + + default: + return lang; + } +} + diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index c1c2b1bdfb7..cb3228cc8be 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -5,14 +5,30 @@ import * as vscode from 'vscode'; -const knownSchemes = ['http:', 'https:', 'file:', 'mailto:', 'data:', `${vscode.env.uriScheme}:`, 'vscode:', 'vscode-insiders:', 'vscode-resource:']; +export const Schemes = { + http: 'http:', + https: 'https:', + file: 'file:', + mailto: 'mailto:', + data: 'data:', + vscode: 'vscode:', + 'vscode-insiders': 'vscode-insiders:', + 'vscode-resource': 'vscode-resource', +}; -export function getUriForLinkWithKnownExternalScheme( - link: string, -): vscode.Uri | undefined { - if (knownSchemes.some(knownScheme => link.toLowerCase().startsWith(knownScheme))) { +const knownSchemes = [ + ...Object.values(Schemes), + `${vscode.env.uriScheme}:` +]; + +export function getUriForLinkWithKnownExternalScheme(link: string): vscode.Uri | undefined { + if (knownSchemes.some(knownScheme => isOfScheme(knownScheme, link))) { return vscode.Uri.parse(link); } return undefined; } + +export function isOfScheme(scheme: string, link: string): boolean { + return link.toLowerCase().startsWith(scheme); +} diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 38058db7c75..94db8a807fe 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -28,7 +28,7 @@ The extension supports running a script as a task from the Explorer. Right-click ### Others -The extension fetches data from https://registry.npmjs/org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies. +The extension fetches data from https://registry.npmjs.org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies. ## Settings diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 90520b575c4..3cca674fc6e 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -7,8 +7,8 @@ "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.", "config.npm.scriptExplorerAction": "The default click action used in the scripts explorer: `open` or `run`, the default is `open`.", - "config.npm.fetchOnlinePackageInfo": "Fetch data from https://registry.npmjs/org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies.", "config.npm.enableRunFromFolderContextMenu": "Enable running scripts from the context menu of a folder in Explorer", + "config.npm.fetchOnlinePackageInfo": "Fetch data from https://registry.npmjs.org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies.", "npm.parseError": "Npm task detection: failed to parse the file {0}", "taskdef.script": "The npm script to customize.", "taskdef.path": "The path to the folder of the package.json file that provides the script. Can be omitted.", diff --git a/extensions/php-language-features/src/features/validationProvider.ts b/extensions/php-language-features/src/features/validationProvider.ts index 6198974da88..c86a1f780ea 100644 --- a/extensions/php-language-features/src/features/validationProvider.ts +++ b/extensions/php-language-features/src/features/validationProvider.ts @@ -13,6 +13,13 @@ import { ThrottledDelayer } from './utils/async'; import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); +const enum Setting { + Run = 'php.validate.run', + CheckedExecutablePath = 'php.validate.checkedExecutablePath', + Enable = 'php.validate.enable', + ExecutablePath = 'php.validate.executablePath', +} + export class LineDecoder { private stringDecoder: NodeStringDecoder; private remaining: string | null; @@ -78,8 +85,6 @@ namespace RunTrigger { }; } -const CheckedExecutablePath = 'php.validate.checkedExecutablePath'; - export default class PHPValidationProvider { private static MatchExpression: RegExp = /(?:(?:Parse|Fatal) error): (.*)(?: in )(.*?)(?: on line )(\d+)/; @@ -129,11 +134,11 @@ export default class PHPValidationProvider { } private loadConfiguration(): void { - let section = vscode.workspace.getConfiguration('php'); + let section = vscode.workspace.getConfiguration(); let oldExecutable = this.executable; if (section) { - this.validationEnabled = section.get('validate.enable', true); - let inspect = section.inspect('validate.executablePath'); + this.validationEnabled = section.get(Setting.Enable, true); + let inspect = section.inspect(Setting.ExecutablePath); if (inspect && inspect.workspaceValue) { this.executable = inspect.workspaceValue; this.executableIsUserDefined = false; @@ -144,9 +149,9 @@ export default class PHPValidationProvider { this.executable = undefined; this.executableIsUserDefined = undefined; } - this.trigger = RunTrigger.from(section.get('validate.run', RunTrigger.strings.onSave)); + this.trigger = RunTrigger.from(section.get(Setting.Run, RunTrigger.strings.onSave)); } - if (this.executableIsUserDefined !== true && this.workspaceStore.get(CheckedExecutablePath, undefined) !== undefined) { + if (this.executableIsUserDefined !== true && this.workspaceStore.get(Setting.CheckedExecutablePath, undefined) !== undefined) { vscode.commands.executeCommand('setContext', 'php.untrustValidationExecutableContext', true); } this.delayers = Object.create(null); @@ -172,7 +177,7 @@ export default class PHPValidationProvider { } private untrustValidationExecutable() { - this.workspaceStore.update(CheckedExecutablePath, undefined); + this.workspaceStore.update(Setting.CheckedExecutablePath, undefined); vscode.commands.executeCommand('setContext', 'php.untrustValidationExecutableContext', false); } @@ -196,7 +201,7 @@ export default class PHPValidationProvider { }; if (this.executableIsUserDefined !== undefined && !this.executableIsUserDefined) { - let checkedExecutablePath = this.workspaceStore.get(CheckedExecutablePath, undefined); + let checkedExecutablePath = this.workspaceStore.get(Setting.CheckedExecutablePath, undefined); if (!checkedExecutablePath || checkedExecutablePath !== this.executable) { vscode.window.showInformationMessage( localize('php.useExecutablePath', 'Do you allow {0} (defined as a workspace setting) to be executed to lint PHP files?', this.executable), @@ -213,7 +218,7 @@ export default class PHPValidationProvider { if (!selected || selected.id === 'no') { this.pauseValidation = true; } else if (selected.id === 'yes') { - this.workspaceStore.update(CheckedExecutablePath, this.executable); + this.workspaceStore.update(Setting.CheckedExecutablePath, this.executable); vscode.commands.executeCommand('setContext', 'php.untrustValidationExecutableContext', true); trigger(); } @@ -286,7 +291,7 @@ export default class PHPValidationProvider { }); } - private showError(error: any, executable: string): void { + private async showError(error: any, executable: string): Promise { let message: string | null = null; if (error.code === 'ENOENT') { if (this.executable) { @@ -297,8 +302,13 @@ export default class PHPValidationProvider { } else { message = error.message ? error.message : localize('unknownReason', 'Failed to run php using path: {0}. Reason is unknown.', executable); } - if (message) { - vscode.window.showInformationMessage(message); + if (!message) { + return; + } + + const openSettings = localize('goToSetting', 'Open Settings'); + if (await vscode.window.showInformationMessage(message, openSettings) === openSettings) { + vscode.commands.executeCommand('workbench.action.openSettings', Setting.ExecutablePath); } } } diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index 2eb11e31045..b9b67d2be50 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -25,8 +25,8 @@ ["`", "`"] ], "indentationRules": { - "increaseIndentPattern": "({(?!.+}).*|\\(|\\[|((else(\\s)?)?if|else|for(each)?|while|switch).*:)\\s*(/[/*].*)?$", - "decreaseIndentPattern": "^(.*\\*\\/)?\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else:)|\\b((end(if|for(each)?|while|switch));))" + "increaseIndentPattern": "({(?!.*}).*|\\(|\\[|((else(\\s)?)?if|else|for(each)?|while|switch|case).*:)\\s*((/[/*].*|)?$|\\?>)", + "decreaseIndentPattern": "^(.*\\*\\/)?\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else:)|\\b((end(if|for(each)?|while|switch)|break);))" }, "folding": { "markers": { @@ -34,4 +34,4 @@ "end": "^\\s*(#|\/\/)endregion\\b" } } -} \ No newline at end of file +} diff --git a/extensions/rust/language-configuration.json b/extensions/rust/language-configuration.json index 6f198f44f4e..bd4cbb8d8f0 100644 --- a/extensions/rust/language-configuration.json +++ b/extensions/rust/language-configuration.json @@ -21,6 +21,10 @@ ["\"", "\""], ["'", "'"] ], + "indentationRules": { + "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$", + "decreaseIndentPattern": "^\\s*(\\s*\\/[*].*[*]\\/\\s*)*[})]" + }, "folding": { "markers": { "start": "^\\s*//\\s*#?region\\b", diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index a55af2b08ff..211401a070c 100644 --- a/extensions/shellscript/package.json +++ b/extensions/shellscript/package.json @@ -23,6 +23,12 @@ "language": "shellscript", "scopeName": "source.shell", "path": "./syntaxes/shell-unix-bash.tmLanguage.json" - }] + }], + "configurationDefaults": { + "[shellscript]": { + "files.eol": "\n" + } + } + } } diff --git a/extensions/sql/language-configuration.json b/extensions/sql/language-configuration.json index a9c154bcec8..cf96472ffd8 100644 --- a/extensions/sql/language-configuration.json +++ b/extensions/sql/language-configuration.json @@ -12,8 +12,9 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "N'", "close": "'", "notIn": ["string", "comment"] }, + { "open": "'", "close": "'", "notIn": ["string", "comment"] } ], "surroundingPairs": [ ["{", "}"], diff --git a/extensions/typescript-basics/package.json b/extensions/typescript-basics/package.json index 185a8a2bc71..0d162fcbaa2 100644 --- a/extensions/typescript-basics/package.json +++ b/extensions/typescript-basics/package.json @@ -98,36 +98,6 @@ "language": "typescriptreact", "path": "./snippets/typescript.json" } - ], - "jsonValidation": [ - { - "fileMatch": "tsconfig.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" - }, - { - "fileMatch": "tsconfig.json", - "url": "./schemas/tsconfig.schema.json" - }, - { - "fileMatch": "tsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" - }, - { - "fileMatch": "tsconfig-*.json", - "url": "./schemas/tsconfig.schema.json" - }, - { - "fileMatch": "tsconfig-*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" - }, - { - "fileMatch": "tsconfig.*.json", - "url": "./schemas/tsconfig.schema.json" - }, - { - "fileMatch": "typings.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/typings.json" - } ] } } diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 0b18f854feb..5b9d01c69d1 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -53,6 +53,62 @@ { "fileMatch": "package.json", "url": "./schemas/package.schema.json" + }, + { + "fileMatch": "tsconfig.json", + "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + }, + { + "fileMatch": "tsconfig.json", + "url": "./schemas/tsconfig.schema.json" + }, + { + "fileMatch": "tsconfig.*.json", + "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + }, + { + "fileMatch": "tsconfig-*.json", + "url": "./schemas/tsconfig.schema.json" + }, + { + "fileMatch": "tsconfig-*.json", + "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + }, + { + "fileMatch": "tsconfig.*.json", + "url": "./schemas/tsconfig.schema.json" + }, + { + "fileMatch": "typings.json", + "url": "https://schemastore.azurewebsites.net/schemas/json/typings.json" + }, + { + "fileMatch": ".bowerrc", + "url": "https://schemastore.azurewebsites.net/schemas/json/bowerrc.json" + }, + { + "fileMatch": ".babelrc", + "url": "https://schemastore.azurewebsites.net/schemas/json/babelrc.json" + }, + { + "fileMatch": ".babelrc.json", + "url": "https://schemastore.azurewebsites.net/schemas/json/babelrc.json" + }, + { + "fileMatch": "jsconfig.json", + "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" + }, + { + "fileMatch": "jsconfig.json", + "url": "./schemas/jsconfig.schema.json" + }, + { + "fileMatch": "jsconfig.*.json", + "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" + }, + { + "fileMatch": "jsconfig.*.json", + "url": "./schemas/jsconfig.schema.json" } ], "configuration": { diff --git a/extensions/javascript/schemas/jsconfig.schema.json b/extensions/typescript-language-features/schemas/jsconfig.schema.json similarity index 100% rename from extensions/javascript/schemas/jsconfig.schema.json rename to extensions/typescript-language-features/schemas/jsconfig.schema.json diff --git a/extensions/typescript-basics/schemas/tsconfig.schema.json b/extensions/typescript-language-features/schemas/tsconfig.schema.json similarity index 100% rename from extensions/typescript-basics/schemas/tsconfig.schema.json rename to extensions/typescript-language-features/schemas/tsconfig.schema.json diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts index 7dba2cfa93c..fba9348435c 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts @@ -6,7 +6,7 @@ import 'mocha'; import * as assert from 'assert'; import { join } from 'path'; -import { commands, workspace, window, Uri, ViewColumn, Range, Position } from 'vscode'; +import { commands, workspace, window, Uri, Range, Position, ViewColumn } from 'vscode'; suite('commands namespace tests', () => { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts index 7f21223b344..525b5a9318c 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts @@ -10,7 +10,7 @@ suite('workspace-namespace', () => { suite('Tasks', () => { - test('CustomExecution2 task should start and shutdown successfully', (done) => { + test('CustomExecution task should start and shutdown successfully', (done) => { interface CustomTestingTaskDefinition extends vscode.TaskDefinition { /** * One of the task properties. This can be used to customize the task in the tasks.json @@ -35,7 +35,7 @@ suite('workspace-namespace', () => { customProp1: 'testing task one' }; const writeEmitter = new vscode.EventEmitter(); - const execution = new vscode.CustomExecution2((): Thenable => { + const execution = new vscode.CustomExecution((): Thenable => { const pty: vscode.Pseudoterminal = { onDidWrite: writeEmitter.event, open: () => { diff --git a/package.json b/package.json index d4deb1c047d..7f469d57bb0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.39.0", - "distro": "afbdc1c13e8f8c6eb97a392e1e98bd9474d4bfef", + "version": "1.40.0", + "distro": "9627858bf882fef220a55f51257dca1491763dc1", "author": { "name": "Microsoft Corporation" }, @@ -28,10 +28,9 @@ "update-distro": "node build/npm/update-distro.js" }, "dependencies": { - "@microsoft/applicationinsights-web": "^2.1.1", "applicationinsights": "1.0.8", "chokidar": "3.1.0", - "graceful-fs": "4.1.11", + "graceful-fs": "4.2.2", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.1", "iconv-lite": "0.5.0", @@ -52,8 +51,8 @@ "vscode-ripgrep": "^1.5.7", "vscode-sqlite3": "4.0.8", "vscode-textmate": "^4.2.2", - "xterm": "4.1.0-beta8", - "xterm-addon-search": "0.2.0", + "xterm": "4.2.0-beta4", + "xterm-addon-search": "0.3.0-beta5", "xterm-addon-web-links": "0.2.0", "yauzl": "^2.9.2", "yazl": "^2.4.3" @@ -97,10 +96,11 @@ "gulp-rename": "^1.2.0", "gulp-replace": "^0.5.4", "gulp-shell": "^0.6.5", - "gulp-tsb": "4.0.4", + "gulp-tsb": "4.0.5", "gulp-tslint": "^8.1.3", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", + "husky": "^0.13.1", "innosetup": "5.6.1", "is": "^3.1.0", "istanbul-lib-coverage": "^2.0.5", @@ -153,4 +153,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} diff --git a/remote/.yarnrc b/remote/.yarnrc index b28191e6bae..1e16cde724c 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,3 +1,3 @@ disturl "http://nodejs.org/dist" -target "10.11.0" +target "12.4.0" runtime "node" diff --git a/remote/package.json b/remote/package.json index 10c377499f1..15aaa8f30b1 100644 --- a/remote/package.json +++ b/remote/package.json @@ -2,11 +2,10 @@ "name": "vscode-reh", "version": "0.0.0", "dependencies": { - "@microsoft/applicationinsights-web": "^2.1.1", "applicationinsights": "1.0.8", "chokidar": "3.1.0", "cookie": "^0.4.0", - "graceful-fs": "4.1.11", + "graceful-fs": "4.2.2", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.1", "iconv-lite": "0.5.0", @@ -21,8 +20,8 @@ "vscode-proxy-agent": "0.4.0", "vscode-ripgrep": "^1.5.7", "vscode-textmate": "^4.2.2", - "xterm": "4.1.0-beta8", - "xterm-addon-search": "0.2.0", + "xterm": "4.2.0-beta4", + "xterm-addon-search": "0.3.0-beta5", "xterm-addon-web-links": "0.2.0", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/web/package.json b/remote/web/package.json index ab86c010ead..db6e5d18e12 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -2,12 +2,11 @@ "name": "vscode-web", "version": "0.0.0", "dependencies": { - "@microsoft/applicationinsights-web": "^2.1.1", "onigasm-umd": "^2.2.2", "semver-umd": "^5.5.3", "vscode-textmate": "^4.2.2", - "xterm": "4.1.0-beta8", - "xterm-addon-search": "0.2.0", + "xterm": "4.2.0-beta4", + "xterm-addon-search": "0.3.0-beta5", "xterm-addon-web-links": "0.2.0" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index ccc09170f6f..759deded50b 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -2,69 +2,6 @@ # yarn lockfile v1 -"@microsoft/applicationinsights-analytics-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.1.1.tgz#6d09c1915f808026e2d45165d04802f09affed59" - integrity sha512-VKIutoFKY99CyKwxLUuj6Vnq14/QwXo9/QSQDpYnHEjo+uKn7QmLsHqWw0K9uYNfNAXt4BZimX/zDg6jZtzeXg== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-channel-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.1.1.tgz#e205eddd93e49d17d9e0711a612b4bfc9810888f" - integrity sha512-fYr9IAqtaEr9AmaPaL3SLQVT3t3GQzl+n74gpNKyAVakDIm0nYQ/bimjdcAhJMDf1VGNSPg/xICneyuZg7Wxlg== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-common@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.1.1.tgz#27e6074584a7a3a8ca3f11f7ff2b7ff0f395bf2d" - integrity sha512-2hkS1Ia1FmAjCuYZ5JlG20/WgObqdsKtmK5YALAFGHIB4KSQ/Za1qazS+7GsG+E0F9UJivNWL1geUIcNqg5Qjg== - dependencies: - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-core-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.1.1.tgz#30fb6a519cc1c6119c419c4811ce72c260217d9e" - integrity sha512-4t4wf6SKqIcWEQDPg/uOhm+BxtHhu/AFreyEoYZmMfcxzAu33h1FtTQRtxBNbYH1+thiNZCh80yUpnT7d9Hrlw== - dependencies: - tslib "^1.9.3" - -"@microsoft/applicationinsights-dependencies-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.1.1.tgz#8154c3efcb24617d015d0bce7c2cc47797a8d3c4" - integrity sha512-yhb4EToBp+aI+qLo0h5NDNtoo3sDFV60uyIOK843YjzXqVotcXX/lRShlghTkJtYH09QhrdzDjViUHnD4sMFSQ== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-properties-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.1.1.tgz#ca34232766eb16167b5d87693e2ae5d94f2a1559" - integrity sha512-8l+/ppw6xKTam2RL4EHZ52Lcf217olw81j6kyBNKtIcGwSnLNHrFwEeF3vBWIteG2JKzlg1GhGjrkB3oxXsV2g== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-web@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.1.1.tgz#1a44eddda7c244b88d9eb052dab6c855682e4f05" - integrity sha512-crvhCkNsNxkFuPWmttyWNSAA96D5FxBtKS6UA9MV9f9XHevTfchf/E3AuU9JZcsXufWMQLwLrUQ9ZiA1QJ0EWA== - dependencies: - "@microsoft/applicationinsights-analytics-js" "2.1.1" - "@microsoft/applicationinsights-channel-js" "2.1.1" - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - "@microsoft/applicationinsights-dependencies-js" "2.1.1" - "@microsoft/applicationinsights-properties-js" "2.1.1" - nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -87,11 +24,6 @@ semver-umd@^5.5.3: resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== -tslib@^1.9.3: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - vscode-textmate@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.2.2.tgz#0b4dabc69a6fba79a065cb6b615f66eac07c8f4c" @@ -99,17 +31,17 @@ vscode-textmate@^4.2.2: dependencies: oniguruma "^7.2.0" -xterm-addon-search@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0.tgz#46659c7c33f9fc268ad3e7e6c5824bb2fdb65852" - integrity sha512-C/v2VvFn3hb1qUgjJPo7LxzxNCLBgNJv8n6v/bH2NqPz32/PNUF+IHu0SFf1TaIH+pydUpKXCtob5a/UyZg/+Q== +xterm-addon-search@0.3.0-beta5: + version "0.3.0-beta5" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta5.tgz#fd53d33a77a0235018479c712be8c12f7c0d083a" + integrity sha512-3GkGc4hST35/4hzgnQPLLvQ29WH7MkZ0mUrBE/Vm1IQum7TnMvWPTkGemwM+wAl4tdBmynNccHJlFeQzaQtVUg== xterm-addon-web-links@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.0.tgz#b408a0be46211d8d4a0bb5e701d8f3c2bd07d473" integrity sha512-dq81c4Pzli2PgKVBgY2REte9sCVibR3df8AP3SEvCTM9uYFnUFxtxzMTplPnc7+rXabVhFdbU6x+rstIk8HNQg== -xterm@4.1.0-beta8: - version "4.1.0-beta8" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.1.0-beta8.tgz#c1ef323ba336d92f5b52302b66f672dfff75b3ef" - integrity sha512-6lf+XVv0qT285w49P92tSYoUB406jdbgdhnPKNzxCIGtGX8kcwK+pHZ8HncDwcEhmTmI4LZ/WXPGtOQJg+onwg== +xterm@4.2.0-beta4: + version "4.2.0-beta4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta4.tgz#596577f94a1da372119d192363ea2b19d1f8b50c" + integrity sha512-BmkpxCpqdOJoNdIcddkRT8S65sGjgBbWI0uIJNSnzZvj81OKcraMSTmF/ODw0TF/MDLc33Fx9cpDx/D6lQgl8Q== diff --git a/remote/yarn.lock b/remote/yarn.lock index 052c1fa7847..b018aafbaab 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -2,69 +2,6 @@ # yarn lockfile v1 -"@microsoft/applicationinsights-analytics-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.1.1.tgz#6d09c1915f808026e2d45165d04802f09affed59" - integrity sha512-VKIutoFKY99CyKwxLUuj6Vnq14/QwXo9/QSQDpYnHEjo+uKn7QmLsHqWw0K9uYNfNAXt4BZimX/zDg6jZtzeXg== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-channel-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.1.1.tgz#e205eddd93e49d17d9e0711a612b4bfc9810888f" - integrity sha512-fYr9IAqtaEr9AmaPaL3SLQVT3t3GQzl+n74gpNKyAVakDIm0nYQ/bimjdcAhJMDf1VGNSPg/xICneyuZg7Wxlg== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-common@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.1.1.tgz#27e6074584a7a3a8ca3f11f7ff2b7ff0f395bf2d" - integrity sha512-2hkS1Ia1FmAjCuYZ5JlG20/WgObqdsKtmK5YALAFGHIB4KSQ/Za1qazS+7GsG+E0F9UJivNWL1geUIcNqg5Qjg== - dependencies: - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-core-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.1.1.tgz#30fb6a519cc1c6119c419c4811ce72c260217d9e" - integrity sha512-4t4wf6SKqIcWEQDPg/uOhm+BxtHhu/AFreyEoYZmMfcxzAu33h1FtTQRtxBNbYH1+thiNZCh80yUpnT7d9Hrlw== - dependencies: - tslib "^1.9.3" - -"@microsoft/applicationinsights-dependencies-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.1.1.tgz#8154c3efcb24617d015d0bce7c2cc47797a8d3c4" - integrity sha512-yhb4EToBp+aI+qLo0h5NDNtoo3sDFV60uyIOK843YjzXqVotcXX/lRShlghTkJtYH09QhrdzDjViUHnD4sMFSQ== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-properties-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.1.1.tgz#ca34232766eb16167b5d87693e2ae5d94f2a1559" - integrity sha512-8l+/ppw6xKTam2RL4EHZ52Lcf217olw81j6kyBNKtIcGwSnLNHrFwEeF3vBWIteG2JKzlg1GhGjrkB3oxXsV2g== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-web@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.1.1.tgz#1a44eddda7c244b88d9eb052dab6c855682e4f05" - integrity sha512-crvhCkNsNxkFuPWmttyWNSAA96D5FxBtKS6UA9MV9f9XHevTfchf/E3AuU9JZcsXufWMQLwLrUQ9ZiA1QJ0EWA== - dependencies: - "@microsoft/applicationinsights-analytics-js" "2.1.1" - "@microsoft/applicationinsights-channel-js" "2.1.1" - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - "@microsoft/applicationinsights-dependencies-js" "2.1.1" - "@microsoft/applicationinsights-properties-js" "2.1.1" - agent-base@4, agent-base@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce" @@ -211,7 +148,12 @@ glob-parent@^5.0.0: dependencies: is-glob "^4.0.1" -graceful-fs@4.1.11, graceful-fs@^4.1.2: +graceful-fs@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" + integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== + +graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= @@ -430,11 +372,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tslib@^1.9.3: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -479,20 +416,20 @@ vscode-windows-registry@1.0.2: resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a" integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA== -xterm-addon-search@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0.tgz#46659c7c33f9fc268ad3e7e6c5824bb2fdb65852" - integrity sha512-C/v2VvFn3hb1qUgjJPo7LxzxNCLBgNJv8n6v/bH2NqPz32/PNUF+IHu0SFf1TaIH+pydUpKXCtob5a/UyZg/+Q== +xterm-addon-search@0.3.0-beta5: + version "0.3.0-beta5" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta5.tgz#fd53d33a77a0235018479c712be8c12f7c0d083a" + integrity sha512-3GkGc4hST35/4hzgnQPLLvQ29WH7MkZ0mUrBE/Vm1IQum7TnMvWPTkGemwM+wAl4tdBmynNccHJlFeQzaQtVUg== xterm-addon-web-links@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.0.tgz#b408a0be46211d8d4a0bb5e701d8f3c2bd07d473" integrity sha512-dq81c4Pzli2PgKVBgY2REte9sCVibR3df8AP3SEvCTM9uYFnUFxtxzMTplPnc7+rXabVhFdbU6x+rstIk8HNQg== -xterm@4.1.0-beta8: - version "4.1.0-beta8" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.1.0-beta8.tgz#c1ef323ba336d92f5b52302b66f672dfff75b3ef" - integrity sha512-6lf+XVv0qT285w49P92tSYoUB406jdbgdhnPKNzxCIGtGX8kcwK+pHZ8HncDwcEhmTmI4LZ/WXPGtOQJg+onwg== +xterm@4.2.0-beta4: + version "4.2.0-beta4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta4.tgz#596577f94a1da372119d192363ea2b19d1f8b50c" + integrity sha512-BmkpxCpqdOJoNdIcddkRT8S65sGjgBbWI0uIJNSnzZvj81OKcraMSTmF/ODw0TF/MDLc33Fx9cpDx/D6lQgl8Q== yauzl@^2.9.2: version "2.10.0" diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 564f13ef95c..516c05e4ee0 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -30,7 +30,7 @@ if [ ! -L $0 ]; then # if path is not a symlink, find relatively VSCODE_PATH="$(dirname $0)/.." else - if which readlink >/dev/null; then + if command -v readlink >/dev/null; then # if readlink exists, follow the symlink and find relatively VSCODE_PATH="$(dirname $(readlink -f $0))/.." else diff --git a/resources/linux/code-url-handler.desktop b/resources/linux/code-url-handler.desktop index 7106e0e0969..b85525fbd04 100644 --- a/resources/linux/code-url-handler.desktop +++ b/resources/linux/code-url-handler.desktop @@ -2,7 +2,7 @@ Name=@@NAME_LONG@@ - URL Handler Comment=Code Editing. Redefined. GenericName=Text Editor -Exec=@@EXEC@@ --open-url %U +Exec=@@EXEC@@ --no-sandbox --open-url %U Icon=@@ICON@@ Type=Application NoDisplay=true diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop index 1273bb2db7c..b975e1094a2 100644 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -2,7 +2,7 @@ Name=@@NAME_LONG@@ Comment=Code Editing. Redefined. GenericName=Text Editor -Exec=@@EXEC@@ --unity-launch %F +Exec=@@EXEC@@ --no-sandbox --unity-launch %F Icon=@@ICON@@ Type=Application StartupNotify=false @@ -14,5 +14,5 @@ Keywords=vscode; [Desktop Action new-empty-window] Name=New Empty Window -Exec=@@EXEC@@ --new-window %F +Exec=@@EXEC@@ --no-sandbox --new-window %F Icon=@@ICON@@ diff --git a/resources/win32/inno-big-125.bmp b/resources/win32/inno-big-125.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-big-150.bmp b/resources/win32/inno-big-150.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-big-175.bmp b/resources/win32/inno-big-175.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-big-200.bmp b/resources/win32/inno-big-200.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-big-225.bmp b/resources/win32/inno-big-225.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-small-100.bmp b/resources/win32/inno-small-100.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-small-125.bmp b/resources/win32/inno-small-125.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-small-150.bmp b/resources/win32/inno-small-150.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-small-175.bmp b/resources/win32/inno-small-175.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-small-200.bmp b/resources/win32/inno-small-200.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-small-225.bmp b/resources/win32/inno-small-225.bmp old mode 100755 new mode 100644 diff --git a/resources/win32/inno-small-250.bmp b/resources/win32/inno-small-250.bmp old mode 100755 new mode 100644 diff --git a/scripts/code-cli.bat b/scripts/code-cli.bat index 6fbf5edd626..56c37567201 100644 --- a/scripts/code-cli.bat +++ b/scripts/code-cli.bat @@ -24,7 +24,7 @@ if "%1"=="--builtin" goto builtin node build\lib\builtInExtensions.js :: Build -if not exist out node .\node_modules\gulp\bin\gulp.js compile +if not exist out yarn compile :: Configuration set ELECTRON_RUN_AS_NODE=1 diff --git a/scripts/code-cli.sh b/scripts/code-cli.sh index a7d61d5a897..e42beeed5bf 100755 --- a/scripts/code-cli.sh +++ b/scripts/code-cli.sh @@ -35,7 +35,7 @@ function code() { node build/lib/builtInExtensions.js # Build - test -d out || ./node_modules/.bin/gulp compile + test -d out || yarn compile ELECTRON_RUN_AS_NODE=1 \ NODE_ENV=development \ diff --git a/scripts/code.bat b/scripts/code.bat index f4689608e4a..0eb0eb0b342 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -24,7 +24,7 @@ if "%1"=="--builtin" goto builtin node build\lib\builtInExtensions.js :: Build -if not exist out node .\node_modules\gulp\bin\gulp.js compile +if not exist out yarn compile :: Configuration set NODE_ENV=development diff --git a/scripts/code.sh b/scripts/code.sh index e82babd818a..0440e49e71a 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -39,7 +39,7 @@ function code() { node build/lib/builtInExtensions.js # Build - test -d out || ./node_modules/.bin/gulp compile + test -d out || yarn compile # Configuration export NODE_ENV=development diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index d557a24edaa..0d7b9ee4b37 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -29,16 +29,18 @@ call .\scripts\test.bat --runGlob **\*.integrationTest.js %* if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in the extension host -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% + +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% + if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% . +call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in commonJS (HTML, CSS, JSON language server tests...) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index df6219221b7..21652b632cf 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -8,7 +8,7 @@ if [[ "$OSTYPE" == "darwin"* ]]; then else ROOT=$(dirname $(dirname $(readlink -f $0))) VSCODEUSERDATADIR=`mktemp -d 2>/dev/null` - LINUX_NO_SANDBOX="" + LINUX_NO_SANDBOX="--no-sandbox" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox. fi cd $ROOT @@ -37,13 +37,14 @@ fi ./scripts/test.sh --runGlob **/*.integrationTest.js "$@" # Tests in the extension host -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR + mkdir -p $ROOT/extensions/emmet/test-fixtures -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR rm -rf $ROOT/extensions/emmet/test-fixtures # Remote Integration Tests diff --git a/scripts/test.sh b/scripts/test.sh index e1ed5aa65c7..630af4e53e5 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -34,5 +34,5 @@ else cd $ROOT ; \ ELECTRON_ENABLE_LOGGING=1 \ "$CODE" \ - test/electron/index.js "$@" + test/electron/index.js --no-sandbox "$@" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox. fi diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 72403b1e413..3999314e9fe 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -21,7 +21,7 @@ exports.assign = function assign(destination, source) { * * @param {string[]} modulePaths * @param {(result, configuration: object) => any} resultCallback - * @param {{ forceEnableDeveloperKeybindings?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options + * @param {{ forceEnableDeveloperKeybindings?: boolean, disallowReloadKeybinding?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options */ exports.load = function (modulePaths, resultCallback, options) { @@ -58,7 +58,7 @@ exports.load = function (modulePaths, resultCallback, options) { const enableDeveloperTools = (process.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; let developerToolsUnbind; if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { - developerToolsUnbind = registerDeveloperKeybindings(); + developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); } // Correctly inherit the parent's environment @@ -159,9 +159,10 @@ function parseURLQueryArgs() { } /** + * @param {boolean} disallowReloadKeybinding * @returns {() => void} */ -function registerDeveloperKeybindings() { +function registerDeveloperKeybindings(disallowReloadKeybinding) { // @ts-ignore const ipc = require('electron').ipcRenderer; @@ -185,7 +186,7 @@ function registerDeveloperKeybindings() { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { ipc.send('vscode:toggleDevTools'); - } else if (key === RELOAD_KB) { + } else if (key === RELOAD_KB && !disallowReloadKeybinding) { ipc.send('vscode:reloadWindow'); } }; diff --git a/src/bootstrap.js b/src/bootstrap.js index 3aeaa72e3b1..bd1aa29cebe 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -21,6 +21,7 @@ process.on('SIGPIPE', () => { //#endregion //#region Add support for redirecting the loading of node modules + exports.injectNodeModuleLookupPath = function (injectPath) { if (!injectPath) { throw new Error('Missing injectPath'); @@ -36,10 +37,8 @@ exports.injectNodeModuleLookupPath = function (injectPath) { const originalResolveLookupPaths = Module._resolveLookupPaths; // @ts-ignore - Module._resolveLookupPaths = function (moduleName, parent, newReturn) { - const result = originalResolveLookupPaths(moduleName, parent, newReturn); - - const paths = newReturn ? result : result[1]; + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); for (let i = 0, len = paths.length; i < len; i++) { if (paths[i] === nodeModulesPath) { paths.splice(i, 0, injectPath); @@ -47,7 +46,7 @@ exports.injectNodeModuleLookupPath = function (injectPath) { } } - return result; + return paths; }; }; //#endregion @@ -71,11 +70,10 @@ exports.enableASARSupport = function (nodeModulesPath) { // @ts-ignore const originalResolveLookupPaths = Module._resolveLookupPaths; - // @ts-ignore - Module._resolveLookupPaths = function (request, parent, newReturn) { - const result = originalResolveLookupPaths(request, parent, newReturn); - const paths = newReturn ? result : result[1]; + // @ts-ignore + Module._resolveLookupPaths = function (request, parent) { + const paths = originalResolveLookupPaths(request, parent); for (let i = 0, len = paths.length; i < len; i++) { if (paths[i] === NODE_MODULES_PATH) { paths.splice(i, 0, NODE_MODULES_ASAR_PATH); @@ -83,7 +81,7 @@ exports.enableASARSupport = function (nodeModulesPath) { } } - return result; + return paths; }; }; //#endregion diff --git a/src/main.js b/src/main.js index 4b0be14bf99..afe479d6174 100644 --- a/src/main.js +++ b/src/main.js @@ -12,12 +12,14 @@ const lp = require('./vs/base/node/languagePacks'); perf.mark('main:started'); const path = require('path'); +const fs = require('fs'); +const os = require('os'); const bootstrap = require('./bootstrap'); const paths = require('./paths'); // @ts-ignore const product = require('../product.json'); // @ts-ignore -const app = require('electron').app; +const { app, protocol } = require('electron'); // Enable portable support const portable = bootstrap.configurePortable(); @@ -25,7 +27,7 @@ const portable = bootstrap.configurePortable(); // Enable ASAR support bootstrap.enableASARSupport(); -// Set userData path before app 'ready' event and call to process.chdir +// Set userData path before app 'ready' event const args = parseCLIArgs(); const userDataPath = getUserDataPath(args); app.setPath('userData', userDataPath); @@ -33,29 +35,33 @@ app.setPath('userData', userDataPath); // Update cwd based on environment and platform setCurrentWorkingDirectory(); +// Register custom schemes with privileges +protocol.registerSchemesAsPrivileged([ + { scheme: 'vscode-resource', privileges: { secure: true, supportFetchAPI: true, corsEnabled: true } } +]); + // Global app listeners registerListeners(); -/** - * Support user defined locale - * - * @type {Promise} - */ -let nlsConfiguration = undefined; -const userDefinedLocale = getUserDefinedLocale(); -const metaDataFile = path.join(__dirname, 'nls.metadata.json'); - -userDefinedLocale.then(locale => { - if (locale && !nlsConfiguration) { - nlsConfiguration = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); - } -}); - // Cached data const nodeCachedDataDir = getNodeCachedDir(); -// Configure command line switches -configureCommandlineSwitches(args); +// Configure static command line arguments +const argvConfig = configureCommandlineSwitchesSync(args); + +/** + * Support user defined locale: load it early before app('ready') + * to have more things running in parallel. + * + * @type {Promise} nlsConfig | undefined + */ +let nlsConfigurationPromise = undefined; + +const metaDataFile = path.join(__dirname, 'nls.metadata.json'); +const locale = getUserDefinedLocale(argvConfig); +if (locale) { + nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); +} // Load our code once ready app.once('ready', function () { @@ -74,62 +80,35 @@ app.once('ready', function () { } }); -function onReady() { +/** + * Main startup routine + * + * @param {string | undefined} cachedDataDir + * @param {import('./vs/base/node/languagePacks').NLSConfiguration} nlsConfig + */ +function startup(cachedDataDir, nlsConfig) { + nlsConfig._languagePackSupport = true; + + process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); + process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || ''; + + // Load main in AMD + perf.mark('willLoadMainBundle'); + require('./bootstrap-amd').load('vs/code/electron-main/main', () => { + perf.mark('didLoadMainBundle'); + }); +} + +async function onReady() { perf.mark('main:appReady'); - Promise.all([nodeCachedDataDir.ensureExists(), userDefinedLocale]).then(([cachedDataDir, locale]) => { - if (locale && !nlsConfiguration) { - nlsConfiguration = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); - } + try { + const [cachedDataDir, nlsConfig] = await Promise.all([nodeCachedDataDir.ensureExists(), resolveNlsConfiguration()]); - if (!nlsConfiguration) { - nlsConfiguration = Promise.resolve(undefined); - } - - // First, we need to test a user defined locale. If it fails we try the app locale. - // If that fails we fall back to English. - nlsConfiguration.then(nlsConfig => { - - const startup = nlsConfig => { - nlsConfig._languagePackSupport = true; - process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); - process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || ''; - - // Load main in AMD - perf.mark('willLoadMainBundle'); - require('./bootstrap-amd').load('vs/code/electron-main/main', () => { - perf.mark('didLoadMainBundle'); - }); - }; - - // We received a valid nlsConfig from a user defined locale - if (nlsConfig) { - startup(nlsConfig); - } - - // Try to use the app locale. Please note that the app locale is only - // valid after we have received the app ready event. This is why the - // code is here. - else { - let appLocale = app.getLocale(); - if (!appLocale) { - startup({ locale: 'en', availableLanguages: {} }); - } else { - - // See above the comment about the loader and case sensitiviness - appLocale = appLocale.toLowerCase(); - - lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale).then(nlsConfig => { - if (!nlsConfig) { - nlsConfig = { locale: appLocale, availableLanguages: {} }; - } - - startup(nlsConfig); - }); - } - } - }); - }, console.error); + startup(cachedDataDir, nlsConfig); + } catch (error) { + console.error(error); + } } /** @@ -137,16 +116,147 @@ function onReady() { * * @param {ParsedArgs} cliArgs */ -function configureCommandlineSwitches(cliArgs) { +function configureCommandlineSwitchesSync(cliArgs) { + const SUPPORTED_ELECTRON_SWITCHES = [ - // Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791) - app.commandLine.appendSwitch('disable-color-correct-rendering'); + // alias from us for --disable-gpu + 'disable-hardware-acceleration', + + // provided by Electron + 'disable-color-correct-rendering' + ]; + + // Read argv config + const argvConfig = readArgvConfigSync(); + + // Append each flag to Electron + Object.keys(argvConfig).forEach(argvKey => { + if (SUPPORTED_ELECTRON_SWITCHES.indexOf(argvKey) === -1) { + return; // unsupported argv key + } + + const argvValue = argvConfig[argvKey]; + if (argvValue === true || argvValue === 'true') { + if (argvKey === 'disable-hardware-acceleration') { + app.disableHardwareAcceleration(); // needs to be called explicitly + } else { + app.commandLine.appendArgument(argvKey); + } + } else { + app.commandLine.appendSwitch(argvKey, argvValue); + } + }); // Support JS Flags const jsFlags = getJSFlags(cliArgs); if (jsFlags) { - app.commandLine.appendSwitch('--js-flags', jsFlags); + app.commandLine.appendSwitch('js-flags', jsFlags); } + + return argvConfig; +} + +function readArgvConfigSync() { + + // Read or create the argv.json config file sync before app('ready') + const argvConfigPath = getArgvConfigPath(); + let argvConfig; + try { + argvConfig = JSON.parse(stripComments(fs.readFileSync(argvConfigPath).toString())); + } catch (error) { + if (error && error.code === 'ENOENT') { + createDefaultArgvConfigSync(argvConfigPath); + } else { + console.warn(`Unable to read argv.json configuration file in ${argvConfigPath}, falling back to defaults (${error})`); + } + } + + // Fallback to default + if (!argvConfig) { + argvConfig = { + 'disable-color-correct-rendering': true // Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791) + }; + } + + return argvConfig; +} + +/** + * @param {string} argvConfigPath + */ +function createDefaultArgvConfigSync(argvConfigPath) { + try { + + // Ensure argv config parent exists + const argvConfigPathDirname = path.dirname(argvConfigPath); + if (!fs.existsSync(argvConfigPathDirname)) { + fs.mkdirSync(argvConfigPathDirname); + } + + // Migrate over legacy locale + const localeConfigPath = path.join(userDataPath, 'User', 'locale.json'); + const legacyLocale = getLegacyUserDefinedLocaleSync(localeConfigPath); + if (legacyLocale) { + try { + fs.unlinkSync(localeConfigPath); + } catch (error) { + //ignore + } + } + + // Default argv content + const defaultArgvConfigContent = [ + '// This configuration file allows to pass permanent command line arguments to VSCode.', + '// Only a subset of arguments is currently supported to reduce the likelyhood of breaking', + '// the installation.', + '//', + '// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT', + '//', + '// If the command line argument does not have any values, simply assign', + '// it in the JSON below with a value of \'true\'. Otherwise, put the value', + '// directly.', + '//', + '// If you see rendering issues in VSCode and have a better experience', + '// with software rendering, you can configure this by adding:', + '//', + '// \'disable-hardware-acceleration\': true', + '//', + '// NOTE: Changing this file requires a restart of VSCode.', + '{', + ' // Enabled by default by VSCode to resolve color issues in the renderer', + ' // See https://github.com/Microsoft/vscode/issues/51791 for details', + ' "disable-color-correct-rendering": true' + ]; + + if (legacyLocale) { + defaultArgvConfigContent[defaultArgvConfigContent.length - 1] = `${defaultArgvConfigContent[defaultArgvConfigContent.length - 1]},`; // append trailing "," + + defaultArgvConfigContent.push(''); + defaultArgvConfigContent.push(' // Display language of VSCode'); + defaultArgvConfigContent.push(` "locale": "${legacyLocale}"`); + } + + defaultArgvConfigContent.push('}'); + + // Create initial argv.json with default content + fs.writeFileSync(argvConfigPath, defaultArgvConfigContent.join('\n')); + } catch (error) { + console.error(`Unable to create argv.json configuration file in ${argvConfigPath}, falling back to defaults (${error})`); + } +} + +function getArgvConfigPath() { + const vscodePortable = process.env['VSCODE_PORTABLE']; + if (vscodePortable) { + return path.join(vscodePortable, 'argv.json'); + } + + let dataFolderName = product.dataFolderName; + if (process.env['VSCODE_DEV']) { + dataFolderName = `${dataFolderName}-dev`; + } + + return path.join(os.homedir(), dataFolderName, 'argv.json'); } /** @@ -249,7 +359,7 @@ function registerListeners() { } /** - * @returns {{ ensureExists: () => Promise }} + * @returns {{ ensureExists: () => Promise }} */ function getNodeCachedDir() { return new class { @@ -258,8 +368,14 @@ function getNodeCachedDir() { this.value = this._compute(); } - ensureExists() { - return bootstrap.mkdirp(this.value).then(() => this.value, () => { /*ignore*/ }); + async ensureExists() { + try { + await bootstrap.mkdirp(this.value); + + return this.value; + } catch (error) { + // ignore + } } _compute() { @@ -284,6 +400,41 @@ function getNodeCachedDir() { } //#region NLS Support +/** + * Resolve the NLS configuration + * + * @return {Promise} + */ +async function resolveNlsConfiguration() { + + // First, we need to test a user defined locale. If it fails we try the app locale. + // If that fails we fall back to English. + let nlsConfiguration = nlsConfigurationPromise ? await nlsConfigurationPromise : undefined; + if (!nlsConfiguration) { + + // Try to use the app locale. Please note that the app locale is only + // valid after we have received the app ready event. This is why the + // code is here. + let appLocale = app.getLocale(); + if (!appLocale) { + nlsConfiguration = { locale: 'en', availableLanguages: {} }; + } else { + + // See above the comment about the loader and case sensitiviness + appLocale = appLocale.toLowerCase(); + + nlsConfiguration = await lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale); + if (!nlsConfiguration) { + nlsConfiguration = { locale: appLocale, availableLanguages: {} }; + } + } + } else { + // We received a valid nlsConfig from a user defined locale + } + + return nlsConfiguration; +} + /** * @param {string} content * @returns {string} @@ -312,30 +463,36 @@ function stripComments(content) { }); } -// Language tags are case insensitive however an amd loader is case sensitive -// To make this work on case preserving & insensitive FS we do the following: -// the language bundles have lower case language tags and we always lower case -// the locale we receive from the user or OS. /** - * @returns {Promise} + * Language tags are case insensitive however an amd loader is case sensitive + * To make this work on case preserving & insensitive FS we do the following: + * the language bundles have lower case language tags and we always lower case + * the locale we receive from the user or OS. + * + * @param {{ locale: string | undefined; }} argvConfig + * @returns {string | undefined} */ -function getUserDefinedLocale() { +function getUserDefinedLocale(argvConfig) { const locale = args['locale']; if (locale) { - return Promise.resolve(locale.toLowerCase()); + return locale.toLowerCase(); // a directly provided --locale always wins } - const localeConfig = path.join(userDataPath, 'User', 'locale.json'); - return bootstrap.readFile(localeConfig).then(content => { - content = stripComments(content); - try { - const value = JSON.parse(content).locale; - return value && typeof value === 'string' ? value.toLowerCase() : undefined; - } catch (e) { - return undefined; - } - }, () => { - return undefined; - }); + return argvConfig.locale && typeof argvConfig.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined; +} + +/** + * @param {string} localeConfigPath + * @returns {string | undefined} + */ +function getLegacyUserDefinedLocaleSync(localeConfigPath) { + try { + const content = stripComments(fs.readFileSync(localeConfigPath).toString()); + + const value = JSON.parse(content).locale; + return value && typeof value === 'string' ? value.toLowerCase() : undefined; + } catch (error) { + // ignore + } } //#endregion diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 6f44785756c..f1d41613b51 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 4.2.10 +// Type definitions for Electron 6.0.12 // Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -8,6 +8,7 @@ type GlobalEvent = Event; declare namespace Electron { + // TODO: Replace this declaration with NodeJS.EventEmitter class EventEmitter { addListener(event: string, listener: Function): this; on(event: string, listener: Function): this; @@ -21,28 +22,17 @@ declare namespace Electron { listenerCount(type: string): number; prependListener(event: string, listener: Function): this; prependOnceListener(event: string, listener: Function): this; - eventNames(): string[]; + eventNames(): Array<(string | symbol)>; } class Accelerator extends String { } - interface Event extends GlobalEvent { - preventDefault: () => void; - sender: WebContents; - returnValue: any; - ctrlKey?: boolean; - metaKey?: boolean; - shiftKey?: boolean; - altKey?: boolean; - } - interface CommonInterface { clipboard: Clipboard; crashReporter: CrashReporter; nativeImage: typeof NativeImage; - screen: Screen; shell: Shell; } @@ -69,6 +59,7 @@ declare namespace Electron { powerMonitor: PowerMonitor; powerSaveBlocker: PowerSaveBlocker; protocol: Protocol; + screen: Screen; session: typeof Session; systemPreferences: SystemPreferences; TouchBar: typeof TouchBar; @@ -203,9 +194,9 @@ declare namespace Electron { userInfo: any) => void): this; /** * Emitted before the application starts closing its windows. Calling - * event.preventDefault() will prevent the default behaviour, which is terminating + * event.preventDefault() will prevent the default behavior, which is terminating * the application. Note: If application quit was initiated by - * autoUpdater.quitAndInstall() then before-quit is emitted after emitting close + * autoUpdater.quitAndInstall(), then before-quit is emitted after emitting close * event on all windows and closing them. Note: On Windows, this event will not be * emitted if the app is closed due to a shutdown/restart of the system or a user * logout. @@ -372,6 +363,18 @@ declare namespace Electron { * A string with the error's localized description. */ error: string) => void): this; + /** + * Emitted when desktopCapturer.getSources() is called in the renderer process of + * webContents. Calling event.preventDefault() will make it return empty sources. + */ + on(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; + once(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; + addListener(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; + removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; /** * Emitted when the gpu process crashes or is killed. */ @@ -385,7 +388,7 @@ declare namespace Electron { killed: boolean) => void): this; /** * Emitted when webContents wants to do basic auth. The default behavior is to - * cancel all authentications, to override this you should prevent the default + * cancel all authentications. To override this you should prevent the default * behavior with event.preventDefault() and call callback(username, password) with * the credentials. */ @@ -566,13 +569,30 @@ declare namespace Electron { removeListener(event: 'remote-require', listener: (event: Event, webContents: WebContents, moduleName: string) => void): this; + /** + * Emitted when the renderer process of webContents crashes or is killed. + */ + on(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; + once(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; + addListener(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; + removeListener(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; /** * This event will be emitted inside the primary instance of your application when - * a second instance has been executed. argv is an Array of the second instance's - * command line arguments, and workingDirectory is its current working directory. - * Usually applications respond to this by making their primary window focused and - * non-minimized. This event is guaranteed to be emitted after the ready event of - * app gets emitted. + * a second instance has been executed and calls app.requestSingleInstanceLock(). + * argv is an Array of the second instance's command line arguments, and + * workingDirectory is its current working directory. Usually applications respond + * to this by making their primary window focused and non-minimized. This event is + * guaranteed to be emitted after the ready event of app gets emitted. Note: Extra + * command line arguments might be added by Chromium, such as + * --original-process-start-time. */ on(event: 'second-instance', listener: (event: Event, /** @@ -647,8 +667,8 @@ declare namespace Electron { * Emitted when Handoff is about to be resumed on another device. If you need to * update the state to be transferred, you should call event.preventDefault() * immediately, construct a new userInfo dictionary and call - * app.updateCurrentActiviy() in a timely manner. Otherwise the operation will fail - * and continue-activity-error will be called. + * app.updateCurrentActiviy() in a timely manner. Otherwise, the operation will + * fail and continue-activity-error will be called. */ on(event: 'update-activity-state', listener: (event: Event, /** @@ -760,8 +780,8 @@ declare namespace Electron { removeListener(event: 'window-all-closed', listener: Function): this; /** * Adds path to the recent documents list. This list is managed by the OS. On - * Windows you can visit the list from the task bar, and on macOS you can visit it - * from dock menu. + * Windows, you can visit the list from the task bar, and on macOS, you can visit + * it from dock menu. */ addRecentDocument(path: string): void; /** @@ -779,11 +799,6 @@ declare namespace Electron { * before app is ready. */ disableHardwareAcceleration(): void; - /** - * Enables mixed sandbox mode on the app. This method can only be called before app - * is ready. - */ - enableMixedSandbox(): void; /** * Enables full sandbox mode on the app. This method can only be called before app * is ready. @@ -791,8 +806,8 @@ declare namespace Electron { enableSandbox(): void; /** * Exits immediately with exitCode. exitCode defaults to 0. All windows will be - * closed immediately without asking user and the before-quit and will-quit events - * will not be emitted. + * closed immediately without asking the user, and the before-quit and will-quit + * events will not be emitted. */ exit(exitCode?: number): void; /** @@ -808,12 +823,19 @@ declare namespace Electron { * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, options?: FileIconOptions): Promise; /** - * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux - * and macOS, icons depend on the application associated with file mime type. + * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On + * Linux and macOS, icons depend on the application associated with file mime type. + * Deprecated Soon */ getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; + /** + * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On + * Linux and macOS, icons depend on the application associated with file mime type. + * Deprecated Soon + */ + getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; getGPUFeatureStatus(): GPUFeatureStatus; /** * For infoType equal to complete: Promise is fulfilled with Object containing all @@ -828,12 +850,16 @@ declare namespace Electron { /** * To set the locale, you'll want to use a command line switch at app startup, * which may be found here. Note: When distributing your packaged app, you have to - * also ship the locales folder. Note: On Windows you have to call it after the + * also ship the locales folder. Note: On Windows, you have to call it after the * ready events gets emitted. */ getLocale(): string; /** - * If you provided path and args options to app.setLoginItemSettings then you need + * Note: When unable to detect locale country code, it returns empty string. + */ + getLocaleCountryCode(): string; + /** + * If you provided path and args options to app.setLoginItemSettings, then you need * to pass the same arguments here for openAtLogin to be set correctly. */ getLoginItemSettings(options?: LoginItemSettingsOptions): LoginItemSettings; @@ -870,6 +896,9 @@ declare namespace Electron { * Invalidates the current Handoff user activity. */ invalidateCurrentActivity(type: string): void; + /** + * Deprecated Soon + */ isAccessibilitySupportEnabled(): boolean; /** * This method checks if the current executable is the default handler for a @@ -881,15 +910,16 @@ declare namespace Electron { * the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. */ isDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + isEmojiPanelSupported(): boolean; isInApplicationsFolder(): boolean; isReady(): boolean; isUnityRunning(): boolean; /** - * No confirmation dialog will be presented by default, if you wish to allow the - * user to confirm the operation you may do so using the dialog API. NOTE: This + * No confirmation dialog will be presented by default. If you wish to allow the + * user to confirm the operation, you may do so using the dialog API. NOTE: This * method throws errors if anything other than the user causes the move to fail. - * For instance if the user cancels the authorization dialog this method returns - * false. If we fail to perform the copy then this method will throw an error. The + * For instance if the user cancels the authorization dialog, this method returns + * false. If we fail to perform the copy, then this method will throw an error. The * message in the error should be informative and tell you exactly what went wrong */ moveToApplicationsFolder(): boolean; @@ -903,14 +933,14 @@ declare namespace Electron { */ quit(): void; /** - * Relaunches the app when current instance exits. By default the new instance will - * use the same working directory and command line arguments with current instance. - * When args is specified, the args will be passed as command line arguments - * instead. When execPath is specified, the execPath will be executed for relaunch - * instead of current app. Note that this method does not quit the app when - * executed, you have to call app.quit or app.exit after calling app.relaunch to - * make the app restart. When app.relaunch is called for multiple times, multiple - * instances will be started after current instance exited. An example of + * Relaunches the app when current instance exits. By default, the new instance + * will use the same working directory and command line arguments with current + * instance. When args is specified, the args will be passed as command line + * arguments instead. When execPath is specified, the execPath will be executed for + * relaunch instead of current app. Note that this method does not quit the app + * when executed, you have to call app.quit or app.exit after calling app.relaunch + * to make the app restart. When app.relaunch is called for multiple times, + * multiple instances will be started after current instance exited. An example of * restarting current instance immediately and adding a new command line argument * to the new instance: */ @@ -926,27 +956,25 @@ declare namespace Electron { */ removeAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; /** - * This method makes your application a Single Instance Application - instead of - * allowing multiple instances of your app to run, this will ensure that only a - * single instance of your app is running, and other instances signal this instance - * and exit. The return value of this method indicates whether or not this instance - * of your application successfully obtained the lock. If it failed to obtain the - * lock you can assume that another instance of your application is already running - * with the lock and exit immediately. I.e. This method returns true if your - * process is the primary instance of your application and your app should continue - * loading. It returns false if your process should immediately quit as it has - * sent its parameters to another instance that has already acquired the lock. On - * macOS the system enforces single instance automatically when users try to open a - * second instance of your app in Finder, and the open-file and open-url events - * will be emitted for that. However when users start your app in command line the - * system's single instance mechanism will be bypassed and you have to use this + * The return value of this method indicates whether or not this instance of your + * application successfully obtained the lock. If it failed to obtain the lock, + * you can assume that another instance of your application is already running with + * the lock and exit immediately. I.e. This method returns true if your process is + * the primary instance of your application and your app should continue loading. + * It returns false if your process should immediately quit as it has sent its + * parameters to another instance that has already acquired the lock. On macOS, the + * system enforces single instance automatically when users try to open a second + * instance of your app in Finder, and the open-file and open-url events will be + * emitted for that. However when users start your app in command line, the + * system's single instance mechanism will be bypassed, and you have to use this * method to ensure single instance. An example of activating the window of primary * instance when a second instance starts: */ requestSingleInstanceLock(): boolean; /** * Set the about panel options. This will override the values defined in the app's - * .plist file. See the Apple docs for more details. + * .plist file on MacOS. See the Apple docs for more details. On Linux, values must + * be set in order to be shown; there are no defaults. */ setAboutPanelOptions(options: AboutPanelOptionsOptions): void; /** @@ -955,9 +983,17 @@ declare namespace Electron { * accessibility docs for more details. Disabled by default. This API must be * called after the ready event is emitted. Note: Rendering accessibility tree can * significantly affect the performance of your app. It should not be enabled by - * default. + * default. Deprecated Soon */ setAccessibilitySupportEnabled(enabled: boolean): void; + /** + * Sets or creates a directory your app's logs which can then be manipulated with + * app.getPath() or app.setPath(pathName, newPath). Calling app.setAppLogsPath() + * without a path parameter will result in this directory being set to + * /Library/Logs/YourAppName on macOS, and inside the userData directory on Linux + * and Windows. + */ + setAppLogsPath(path?: string): void; /** * Changes the Application User Model ID to id. */ @@ -967,20 +1003,23 @@ declare namespace Electron { * (aka URI scheme). It allows you to integrate your app deeper into the operating * system. Once registered, all links with your-protocol:// will be opened with the * current executable. The whole link, including protocol, will be passed to your - * application as a parameter. On Windows you can provide optional parameters path, - * the path to your executable, and args, an array of arguments to be passed to - * your executable when it launches. Note: On macOS, you can only register + * application as a parameter. On Windows, you can provide optional parameters + * path, the path to your executable, and args, an array of arguments to be passed + * to your executable when it launches. Note: On macOS, you can only register * protocols that have been added to your app's info.plist, which can not be * modified at runtime. You can however change the file with a simple text editor * or script during build time. Please refer to Apple's documentation for details. - * The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme - * internally. + * Note: In a Windows Store environment (when packaged as an appx) this API will + * return true for all calls but the registry key it sets won't be accessible by + * other applications. In order to register your Windows Store application as a + * default protocol handler you must declare the protocol in your manifest. The API + * uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. */ setAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; /** * Sets the counter badge for current app. Setting the count to 0 will hide the - * badge. On macOS it shows on the dock icon. On Linux it only works for Unity - * launcher, Note: Unity launcher requires the existence of a .desktop file to + * badge. On macOS, it shows on the dock icon. On Linux, it only works for Unity + * launcher. Note: Unity launcher requires the existence of a .desktop file to * work, for more information please read Desktop Environment Integration. */ setBadgeCount(count: number): boolean; @@ -1012,12 +1051,12 @@ declare namespace Electron { setName(name: string): void; /** * Overrides the path to a special directory or file associated with name. If the - * path specifies a directory that does not exist, the directory will be created by - * this method. On failure an Error is thrown. You can only override paths of a - * name defined in app.getPath. By default, web pages' cookies and caches will be - * stored under the userData directory. If you want to change this location, you - * have to override the userData path before the ready event of the app module is - * emitted. + * path specifies a directory that does not exist, an Error is thrown. In that + * case, the directory should be created with fs.mkdirSync or similar. You can only + * override paths of a name defined in app.getPath. By default, web pages' cookies + * and caches will be stored under the userData directory. If you want to change + * this location, you have to override the userData path before the ready event of + * the app module is emitted. */ setPath(name: string, path: string): void; /** @@ -1037,10 +1076,14 @@ declare namespace Electron { */ show(): void; /** - * Show the about panel with the values defined in the app's .plist file or with - * the options set via app.setAboutPanelOptions(options). + * Show the app's about panel options. These options can be overridden with + * app.setAboutPanelOptions(options). */ showAboutPanel(): void; + /** + * Show the platform's native emoji picker. + */ + showEmojiPanel(): void; /** * Start accessing a security scoped resource. With this method Electron * applications that are packaged for the Mac App Store may reach outside their @@ -1054,6 +1097,34 @@ declare namespace Electron { */ updateCurrentActivity(type: string, userInfo: any): void; whenReady(): Promise; + /** + * A Boolean property that's true if Chrome's accessibility support is enabled, + * false otherwise. This property will be true if the use of assistive + * technologies, such as screen readers, has been detected. Setting this property + * to true manually enables Chrome's accessibility support, allowing developers to + * expose accessibility switch to users in application settings. See Chromium's + * accessibility docs for more details. Disabled by default. This API must be + * called after the ready event is emitted. Note: Rendering accessibility tree can + * significantly affect the performance of your app. It should not be enabled by + * default. + */ + accessibilitySupportEnabled?: boolean; + /** + * A Boolean which when true disables the overrides that Electron has in place to + * ensure renderer processes are restarted on every navigation. The current + * default value for this property is false. The intention is for these overrides + * to become disabled by default and then at some point in the future this property + * will be removed. This property impacts which native modules you can use in the + * renderer process. For more information on the direction Electron is going with + * renderer process restarts and usage of native modules in the renderer process + * please check out this Tracking Issue. + */ + allowRendererProcessReuse?: boolean; + /** + * A Menu property that return Menu if one has been set and null otherwise. Users + * can pass a Menu to set this property. + */ + applicationMenu?: Menu; commandLine: CommandLine; dock: Dock; /** @@ -1217,7 +1288,8 @@ declare namespace Electron { * media keys or browser commands, as well as the "Back" button built into some * mice on Windows. Commands are lowercased, underscores are replaced with hyphens, * and the APPCOMMAND_ prefix is stripped off. e.g. APPCOMMAND_BROWSER_BACKWARD is - * emitted as browser-backward. + * emitted as browser-backward. The following app commands are explictly supported + * on Linux: */ on(event: 'app-command', listener: (event: Event, command: string) => void): this; @@ -1551,6 +1623,10 @@ declare namespace Electron { * ready event of the app module is emitted. */ static removeExtension(name: string): void; + /** + * Replacement API for setBrowserView supporting work with multi browser views. + */ + addBrowserView(browserView: BrowserView): void; /** * Adds a window as a tab on this window, after the tab for the window instance. */ @@ -1561,11 +1637,22 @@ declare namespace Electron { blur(): void; blurWebView(): void; /** - * Same as webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(callback: (image: NativeImage) => void): void; /** - * Same as webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Omitting rect will capture the + * whole visible page. + */ + capturePage(rect?: Rectangle): Promise; + /** + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; /** @@ -1598,11 +1685,13 @@ declare namespace Electron { focus(): void; focusOnWebView(): void; getBounds(): Rectangle; - /** - * Note: The BrowserView API is currently experimental and may change or be removed - * in future Electron releases. - */ getBrowserView(): (BrowserView) | (null); + /** + * Returns array of BrowserView what was an attached with addBrowserView or + * setBrowserView. Note: The BrowserView API is currently experimental and may + * change or be removed in future Electron releases. + */ + getBrowserViews(): void; getChildWindows(): BrowserWindow[]; getContentBounds(): Rectangle; getContentSize(): number[]; @@ -1626,13 +1715,10 @@ declare namespace Electron { getRepresentedFilename(): string; getSize(): number[]; /** - * Note: The title of web page can be different from the title of the native + * Note: The title of the web page can be different from the title of the native * window. */ getTitle(): string; - /** - * On Windows and Linux always returns true. - */ hasShadow(): boolean; /** * Hides the window. @@ -1684,7 +1770,7 @@ declare namespace Electron { * Same as webContents.loadFile, filePath should be a path to an HTML file relative * to the root of your application. See the webContents docs for more information. */ - loadFile(filePath: string, options?: LoadFileOptions): void; + loadFile(filePath: string, options?: LoadFileOptions): Promise; /** * Same as webContents.loadURL(url[, options]). The url can be a remote address * (e.g. http://) or a path to a local HTML file using the file:// protocol. To @@ -1692,7 +1778,7 @@ declare namespace Electron { * url.format method: You can load a URL using a POST request with URL-encoded data * by doing the following: */ - loadURL(url: string, options?: LoadURLOptions): void; + loadURL(url: string, options?: LoadURLOptions): Promise; /** * Maximizes the window. This will also show (but not focus) the window if it isn't * being displayed already. @@ -1725,6 +1811,11 @@ declare namespace Electron { * Same as webContents.reload. */ reload(): void; + removeBrowserView(browserView: BrowserView): void; + /** + * Remove the window's menu bar. + */ + removeMenu(): void; /** * Restores the window from minimized state to its previous state. */ @@ -1829,7 +1920,7 @@ declare namespace Electron { */ setFullScreenable(fullscreenable: boolean): void; /** - * Sets whether the window should have a shadow. On Windows and Linux does nothing. + * Sets whether the window should have a shadow. */ setHasShadow(hasShadow: boolean): void; /** @@ -1856,8 +1947,7 @@ declare namespace Electron { */ setMaximumSize(width: number, height: number): void; /** - * Sets the menu as the window's menu bar, setting it to null will remove the menu - * bar. + * Sets the menu as the window's menu bar. */ setMenu(menu: (Menu) | (null)): void; /** @@ -2296,12 +2386,12 @@ declare namespace Electron { // Docs: http://electronjs.org/docs/api/clipboard - availableFormats(type?: string): string[]; + availableFormats(type?: 'selection' | 'clipboard'): string[]; /** * Clears the clipboard content. */ - clear(type?: string): void; - has(format: string, type?: string): boolean; + clear(type?: 'selection' | 'clipboard'): void; + has(format: string, type?: 'selection' | 'clipboard'): boolean; read(format: string): string; /** * Returns an Object containing title and url keys representing the bookmark in the @@ -2310,93 +2400,94 @@ declare namespace Electron { */ readBookmark(): ReadBookmark; readBuffer(format: string): Buffer; + /** + * This method uses synchronous IPC when called from the renderer process. The + * cached value is reread from the find pasteboard whenever the application is + * activated. + */ readFindText(): string; - readHTML(type?: string): string; - readImage(type?: string): NativeImage; - readRTF(type?: string): string; - readText(type?: string): string; + readHTML(type?: 'selection' | 'clipboard'): string; + readImage(type?: 'selection' | 'clipboard'): NativeImage; + readRTF(type?: 'selection' | 'clipboard'): string; + readText(type?: 'selection' | 'clipboard'): string; /** * Writes data to the clipboard. */ - write(data: Data, type?: string): void; + write(data: Data, type?: 'selection' | 'clipboard'): void; /** * Writes the title and url into the clipboard as a bookmark. Note: Most apps on * Windows don't support pasting bookmarks into them so you can use clipboard.write * to write both a bookmark and fallback text to the clipboard. */ - writeBookmark(title: string, url: string, type?: string): void; + writeBookmark(title: string, url: string, type?: 'selection' | 'clipboard'): void; /** * Writes the buffer into the clipboard as format. */ - writeBuffer(format: string, buffer: Buffer, type?: string): void; + writeBuffer(format: string, buffer: Buffer, type?: 'selection' | 'clipboard'): void; /** - * Writes the text into the find pasteboard as plain text. This method uses - * synchronous IPC when called from the renderer process. + * Writes the text into the find pasteboard (the pasteboard that holds information + * about the current state of the active application’s find panel) as plain text. + * This method uses synchronous IPC when called from the renderer process. */ writeFindText(text: string): void; /** * Writes markup to the clipboard. */ - writeHTML(markup: string, type?: string): void; + writeHTML(markup: string, type?: 'selection' | 'clipboard'): void; /** * Writes image to the clipboard. */ - writeImage(image: NativeImage, type?: string): void; + writeImage(image: NativeImage, type?: 'selection' | 'clipboard'): void; /** * Writes the text into the clipboard in RTF. */ - writeRTF(text: string, type?: string): void; + writeRTF(text: string, type?: 'selection' | 'clipboard'): void; /** * Writes the text into the clipboard as plain text. */ - writeText(text: string, type?: string): void; + writeText(text: string, type?: 'selection' | 'clipboard'): void; } interface ContentTracing extends EventEmitter { // Docs: http://electronjs.org/docs/api/content-tracing - /** - * Get the current monitoring traced data. Child processes typically cache trace - * data and only rarely flush and send trace data back to the main process. This is - * because it may be an expensive operation to send the trace data over IPC and we - * would like to avoid unneeded runtime overhead from tracing. So, to end tracing, - * we must asynchronously ask all child processes to flush any pending trace data. - * Once all child processes have acknowledged the captureMonitoringSnapshot request - * the callback will be called with a file that contains the traced data. - */ - captureMonitoringSnapshot(resultFilePath: string, callback: (resultFilePath: string) => void): void; /** * Get a set of category groups. The category groups can change as new code paths * are reached. Once all child processes have acknowledged the getCategories - * request the callback is invoked with an array of category groups. + * request the callback is invoked with an array of category groups. Deprecated + * Soon */ getCategories(callback: (categories: string[]) => void): void; + /** + * Get a set of category groups. The category groups can change as new code paths + * are reached. + */ + getCategories(): Promise; /** * Get the maximum usage across processes of trace buffer as a percentage of the * full state. When the TraceBufferUsage value is determined the callback is - * called. + * called. Deprecated Soon */ - getTraceBufferUsage(callback: (value: number, percentage: number) => void): void; + getTraceBufferUsage(callback: (value: number) => void): void; /** - * Start monitoring on all processes. Monitoring begins immediately locally and - * asynchronously on child processes as soon as they receive the startMonitoring - * request. Once all child processes have acknowledged the startMonitoring request - * the callback will be called. + * Get the maximum usage across processes of trace buffer as a percentage of the + * full state. */ - startMonitoring(options: StartMonitoringOptions, callback: Function): void; + getTraceBufferUsage(): Promise; /** * Start recording on all processes. Recording begins immediately locally and * asynchronously on child processes as soon as they receive the EnableRecording * request. The callback will be called once all child processes have acknowledged - * the startRecording request. + * the startRecording request. Deprecated Soon */ startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig), callback: Function): void; /** - * Stop monitoring on all processes. Once all child processes have acknowledged the - * stopMonitoring request the callback is called. + * Start recording on all processes. Recording begins immediately locally and + * asynchronously on child processes as soon as they receive the EnableRecording + * request. */ - stopMonitoring(callback: Function): void; + startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig)): Promise; /** * Stop recording on all processes. Child processes typically cache trace data and * only rarely flush and send trace data back to the main process. This helps to @@ -2406,9 +2497,18 @@ declare namespace Electron { * acknowledged the stopRecording request, callback will be called with a file that * contains the traced data. Trace data will be written into resultFilePath if it * is not empty or into a temporary file. The actual file path will be passed to - * callback if it's not null. + * callback if it's not null. Deprecated Soon */ stopRecording(resultFilePath: string, callback: (resultFilePath: string) => void): void; + /** + * Stop recording on all processes. Child processes typically cache trace data and + * only rarely flush and send trace data back to the main process. This helps to + * minimize the runtime overhead of tracing since sending trace data over IPC can + * be an expensive operation. So, to end tracing, we must asynchronously ask all + * child processes to flush any pending trace data. Trace data will be written into + * resultFilePath if it is not empty or into a temporary file. + */ + stopRecording(resultFilePath: string): Promise; } interface Cookie { @@ -2520,20 +2620,37 @@ declare namespace Electron { /** * Writes any unwritten cookies data to disk. */ + flushStore(): Promise; + /** + * Writes any unwritten cookies data to disk. Deprecated Soon + */ flushStore(callback: Function): void; + /** + * Sends a request to get all cookies matching filter, and resolves a promise with + * the response. + */ + get(filter: Filter): Promise; /** * Sends a request to get all cookies matching filter, callback will be called with - * callback(error, cookies) on complete. + * callback(error, cookies) on complete. Deprecated Soon */ get(filter: Filter, callback: (error: Error, cookies: Cookie[]) => void): void; + /** + * Removes the cookies matching url and name + */ + remove(url: string, name: string): Promise; /** * Removes the cookies matching url and name, callback will called with callback() - * on complete. + * on complete. Deprecated Soon */ remove(url: string, name: string, callback: Function): void; + /** + * Sets a cookie with details. + */ + set(details: Details): Promise; /** * Sets a cookie with details, callback will be called with callback(error) on - * complete. + * complete. Deprecated Soon */ set(details: Details, callback: (error: Error) => void): void; } @@ -2561,15 +2678,15 @@ declare namespace Electron { id: string; } - interface CrashReporter extends EventEmitter { + interface CrashReporter { // Docs: http://electronjs.org/docs/api/crash-reporter /** * Set an extra parameter to be sent with the crash report. The values specified * here will be sent in addition to any values set via the extra option when start - * was called. This API is only available on macOS, if you need to add/update extra - * parameters on Linux and Windows after your first call to start you can call + * was called. This API is only available on macOS and windows, if you need to + * add/update extra parameters on Linux after your first call to start you can call * start again with the updated extra options. */ addExtraParameter(key: string, value: string): void; @@ -2613,13 +2730,10 @@ declare namespace Electron { * reports from them, use process.crashReporter.start instead. Pass the same * options as above along with an additional one called crashesDirectory that * should point to a directory to store the crash reports temporarily. You can test - * this out by calling process.crash() to crash the child process. Note: To collect - * crash reports from child process in Windows, you need to add this extra code as - * well. This will start the process that will monitor and send the crash reports. - * Replace submitURL, productName and crashesDirectory with appropriate values. - * Note: If you need send additional/updated extra parameters after your first call - * start you can call addExtraParameter on macOS or call start again with the - * new/updated extra parameters on Linux and Windows. Note: On macOS, Electron uses + * this out by calling process.crash() to crash the child process. Note: If you + * need send additional/updated extra parameters after your first call start you + * can call addExtraParameter on macOS or call start again with the new/updated + * extra parameters on Linux and Windows. Note: On macOS and windows, Electron uses * a new crashpad client for crash collection and reporting. If you want to enable * crash reporting, initializing crashpad from the main process using * crashReporter.start is required regardless of which process you want to collect @@ -2631,6 +2745,17 @@ declare namespace Electron { start(options: CrashReporterStartOptions): void; } + interface CustomScheme { + + // Docs: http://electronjs.org/docs/api/structures/custom-scheme + + privileges?: Privileges; + /** + * Custom schemes to be registered with options. + */ + scheme: string; + } + class Debugger extends EventEmitter { // Docs: http://electronjs.org/docs/api/debugger @@ -2712,9 +2837,13 @@ declare namespace Electron { detach(): void; isAttached(): boolean; /** - * Send given command to the debugging target. + * Send given command to the debugging target. Deprecated Soon */ sendCommand(method: string, commandParams?: any, callback?: (error: any, result: any) => void): void; + /** + * Send given command to the debugging target. + */ + sendCommand(method: string, commandParams?: any): Promise; } interface DesktopCapturer extends EventEmitter { @@ -2725,15 +2854,22 @@ declare namespace Electron { * Starts gathering information about all available desktop media sources, and * calls callback(error, sources) when finished. sources is an array of * DesktopCapturerSource objects, each DesktopCapturerSource represents a screen or - * an individual window that can be captured. + * an individual window that can be captured. Deprecated Soon */ getSources(options: SourcesOptions, callback: (error: Error, sources: DesktopCapturerSource[]) => void): void; + getSources(options: SourcesOptions): Promise; } interface DesktopCapturerSource { // Docs: http://electronjs.org/docs/api/structures/desktop-capturer-source + /** + * An icon image of the application that owns the window or null if the source has + * a type screen. The size of the icon is not known in advance and depends on what + * the the application provides. + */ + appIcon: NativeImage; /** * A unique identifier that will correspond to the id of the matching returned by * the . On some platforms, this is equivalent to the XX portion of the id field @@ -2770,7 +2906,7 @@ declare namespace Electron { * information, and gives the user the option of trusting/importing the * certificate. If you provide a browserWindow argument the dialog will be attached * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: + * due to the Win32 APIs used: Deprecated Soon */ showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; /** @@ -2780,6 +2916,14 @@ declare namespace Electron { * to the parent window, making it modal. On Windows the options are more limited, * due to the Win32 APIs used: */ + showCertificateTrustDialog(options: CertificateTrustDialogOptions): Promise; + /** + * On macOS, this displays a modal dialog that shows a message and certificate + * information, and gives the user the option of trusting/importing the + * certificate. If you provide a browserWindow argument the dialog will be attached + * to the parent window, making it modal. On Windows the options are more limited, + * due to the Win32 APIs used: Deprecated Soon + */ showCertificateTrustDialog(options: CertificateTrustDialogOptions, callback: Function): void; /** * On macOS, this displays a modal dialog that shows a message and certificate @@ -2788,6 +2932,14 @@ declare namespace Electron { * to the parent window, making it modal. On Windows the options are more limited, * due to the Win32 APIs used: */ + showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions): Promise; + /** + * On macOS, this displays a modal dialog that shows a message and certificate + * information, and gives the user the option of trusting/importing the + * certificate. If you provide a browserWindow argument the dialog will be attached + * to the parent window, making it modal. On Windows the options are more limited, + * due to the Win32 APIs used: Deprecated Soon + */ showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; /** * Displays a modal dialog that shows an error message. This API can be called @@ -2798,73 +2950,140 @@ declare namespace Electron { showErrorBox(title: string, content: string): void; /** * Shows a message box, it will block the process until the message box is closed. - * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. If a callback - * is passed, the dialog will not block the process. The API call will be - * asynchronous and the result will be passed via callback(response). + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. */ - showMessageBox(browserWindow: BrowserWindow, options: MessageBoxOptions, callback?: (response: number, checkboxChecked: boolean) => void): number; + showMessageBox(browserWindow: BrowserWindow, options: MessageBoxOptions): Promise; + /** + * Shows a message box, it will block the process until the message box is closed. + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. + */ + showMessageBox(options: MessageBoxOptions): Promise; /** * Shows a message box, it will block the process until the message box is closed. * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. If a callback - * is passed, the dialog will not block the process. The API call will be - * asynchronous and the result will be passed via callback(response). + * the dialog to attach itself to a parent window, making it modal. */ - showMessageBox(options: MessageBoxOptions, callback?: (response: number, checkboxChecked: boolean) => void): number; + showMessageBoxSync(browserWindow: BrowserWindow, options: MessageBoxSyncOptions): number; + /** + * Shows a message box, it will block the process until the message box is closed. + * It returns the index of the clicked button. The browserWindow argument allows + * the dialog to attach itself to a parent window, making it modal. + */ + showMessageBoxSync(options: MessageBoxSyncOptions): number; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can * be displayed or selected when you want to limit the user to a specific type. For * example: The extensions array should contain extensions without wildcards or * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). If a callback is passed, the - * API call will be asynchronous and the result will be passed via - * callback(filenames). Note: On Windows and Linux an open dialog can not be both a - * file selector and a directory selector, so if you set properties to ['openFile', - * 'openDirectory'] on these platforms, a directory selector will be shown. + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. */ - showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): (string[]) | (undefined); + showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: Function): Promise; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can * be displayed or selected when you want to limit the user to a specific type. For * example: The extensions array should contain extensions without wildcards or * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). If a callback is passed, the - * API call will be asynchronous and the result will be passed via - * callback(filenames). Note: On Windows and Linux an open dialog can not be both a - * file selector and a directory selector, so if you set properties to ['openFile', - * 'openDirectory'] on these platforms, a directory selector will be shown. + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. */ - showOpenDialog(options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): (string[]) | (undefined); + showOpenDialog(options: OpenDialogOptions, callback?: Function): Promise; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. If a callback is passed, - * the API call will be asynchronous and the result will be passed via - * callback(filename). + * be displayed or selected when you want to limit the user to a specific type. For + * example: The extensions array should contain extensions without wildcards or + * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. */ - showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): (string) | (undefined); + showOpenDialogSync(browserWindow: BrowserWindow, options: OpenDialogSyncOptions): (string[]) | (undefined); /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. If a callback is passed, - * the API call will be asynchronous and the result will be passed via - * callback(filename). + * be displayed or selected when you want to limit the user to a specific type. For + * example: The extensions array should contain extensions without wildcards or + * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. */ - showSaveDialog(options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): (string) | (undefined); + showOpenDialogSync(options: OpenDialogSyncOptions): (string[]) | (undefined); + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using + * the asynchronous version is recommended to avoid issues when expanding and + * collapsing the dialog. + */ + showSaveDialog(options: SaveDialogOptions): Promise; + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using + * the asynchronous version is recommended to avoid issues when expanding and + * collapsing the dialog. + */ + showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions): Promise; + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed, see dialog.showOpenDialog for an example. + */ + showSaveDialogSync(options: SaveDialogSyncOptions): (string) | (undefined); + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed, see dialog.showOpenDialog for an example. + */ + showSaveDialogSync(browserWindow: BrowserWindow, options: SaveDialogSyncOptions): (string) | (undefined); } interface Display { // Docs: http://electronjs.org/docs/api/structures/display + /** + * Can be available, unavailable, unknown. + */ + accelerometerSupport: ('available' | 'unavailable' | 'unknown'); bounds: Rectangle; + /** + * The number of bits per pixel. + */ + colorDepth: number; + /** + * represent a color space (three-dimensional object which contains all realizable + * color combinations) for the purpose of color conversions + */ + colorSpace: string; + /** + * The number of bits per color component. + */ + depthPerComponent: number; /** * Unique identifier associated with the display. */ id: number; + /** + * true for an internal display and false for an external display + */ + internal: boolean; + /** + * Whether or not the display is a monochrome display. + */ + monochrome: boolean; /** * Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. */ @@ -2951,6 +3170,7 @@ declare namespace Electron { getLastModifiedTime(): string; getMimeType(): string; getReceivedBytes(): number; + getSaveDialogOptions(): SaveDialogOptions; getSavePath(): string; getStartTime(): number; /** @@ -2977,6 +3197,12 @@ declare namespace Electron { * received bytes and restart the download from the beginning. */ resume(): void; + /** + * This API allows the user to set custom options for the save dialog that opens + * for the download item by default. The API is only available in session's + * will-download callback function. + */ + setSaveDialogOptions(options: SaveDialogOptions): void; /** * The API is only available in session's will-download callback function. If user * doesn't set the save path via the API, Electron will use the original routine to @@ -2985,6 +3211,13 @@ declare namespace Electron { setSavePath(path: string): void; } + interface Event extends GlobalEvent { + + // Docs: http://electronjs.org/docs/api/structures/event + + preventDefault: (() => void); + } + interface FileFilter { // Docs: http://electronjs.org/docs/api/structures/file-filter @@ -3012,7 +3245,17 @@ declare namespace Electron { * on macOS 10.14 Mojave unless the app has been authorized as a trusted * accessibility client: */ - register(accelerator: Accelerator, callback: Function): void; + register(accelerator: Accelerator, callback: Function): boolean; + /** + * Registers a global shortcut of all accelerator items in accelerators. The + * callback is called when any of the registered shortcuts are pressed by the user. + * When a given accelerator is already taken by other applications, this call will + * silently fail. This behavior is intended by operating systems, since they don't + * want applications to fight for global shortcuts. The following accelerators will + * not be registered successfully on macOS 10.14 Mojave unless the app has been + * authorized as a trusted accessibility client: + */ + registerAll(accelerators: string[], callback: Function): void; /** * Unregisters the global shortcut of accelerator. */ @@ -3118,15 +3361,24 @@ declare namespace Electron { */ finishTransactionByDate(date: string): void; /** - * Retrieves the product descriptions. + * Retrieves the product descriptions. Deprecated Soon */ getProducts(productIDs: string[], callback: (products: Product[]) => void): void; + /** + * Retrieves the product descriptions. + */ + getProducts(productIDs: string[]): Promise; getReceiptURL(): string; + /** + * You should listen for the transactions-updated event as soon as possible and + * certainly before you call purchaseProduct. Deprecated Soon + */ + purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; /** * You should listen for the transactions-updated event as soon as possible and * certainly before you call purchaseProduct. */ - purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; + purchaseProduct(productID: string, quantity?: number): Promise; } class IncomingMessage extends EventEmitter { @@ -3228,12 +3480,12 @@ declare namespace Electron { * Listens to channel, when a new message arrives listener would be called with * listener(event, args...). */ - on(channel: string, listener: Function): this; + on(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this; /** * Adds a one time listener function for the event. This listener is invoked only * the next time a message is sent to channel, after which it is removed. */ - once(channel: string, listener: Function): this; + once(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this; /** * Removes listeners of the specified channel. */ @@ -3245,6 +3497,31 @@ declare namespace Electron { removeListener(channel: string, listener: Function): this; } + interface IpcMainEvent extends Event { + + // Docs: http://electronjs.org/docs/api/structures/ipc-main-event + + /** + * The ID of the renderer frame that sent this message + */ + frameId: number; + /** + * A function that will send an IPC message to the renderer frame that sent the + * original message that you are currently handling. You should use this method to + * "reply" to the sent message in order to guaruntee the reply will go to the + * correct process and frame. + */ + reply: Function; + /** + * Set this to the value to be returned in a syncronous message + */ + returnValue: any; + /** + * Returns the webContents that sent the message + */ + sender: WebContents; + } + interface IpcRenderer extends EventEmitter { // Docs: http://electronjs.org/docs/api/ipc-renderer @@ -3253,12 +3530,12 @@ declare namespace Electron { * Listens to channel, when a new message arrives listener would be called with * listener(event, args...). */ - on(channel: string, listener: Function): this; + on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): this; /** * Adds a one time listener function for the event. This listener is invoked only * the next time a message is sent to channel, after which it is removed. */ - once(channel: string, listener: Function): this; + once(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): this; /** * Removes all listeners, or those of the specified channel. */ @@ -3295,6 +3572,23 @@ declare namespace Electron { sendToHost(channel: string, ...args: any[]): void; } + interface IpcRendererEvent extends Event { + + // Docs: http://electronjs.org/docs/api/structures/ipc-renderer-event + + /** + * The IpcRenderer instance that emitted the event originally + */ + sender: IpcRenderer; + /** + * The webContents.id that sent the message, you can call + * event.sender.sendTo(event.senderId, ...) to reply to the message, see for more + * information. This only applies to messages sent from a different renderer. + * Messages sent directly from the main process set event.senderId to 0. + */ + senderId: number; + } + interface JumpListCategory { // Docs: http://electronjs.org/docs/api/structures/jump-list-category @@ -3358,6 +3652,37 @@ declare namespace Electron { * One of the following: */ type?: ('task' | 'separator' | 'file'); + /** + * The working directory. Default is empty. + */ + workingDirectory?: string; + } + + interface KeyboardEvent extends Event { + + // Docs: http://electronjs.org/docs/api/structures/keyboard-event + + /** + * whether an Alt key was used in an accelerator to trigger the Event + */ + altKey?: boolean; + /** + * whether the Control key was used in an accelerator to trigger the Event + */ + ctrlKey?: boolean; + /** + * whether a meta key was used in an accelerator to trigger the Event + */ + metaKey?: boolean; + /** + * whether a Shift key was used in an accelerator to trigger the Event + */ + shiftKey?: boolean; + /** + * whether an accelerator was used to trigger the event as opposed to another user + * gesture like mouse click + */ + triggeredByAccelerator?: boolean; } interface MemoryUsageDetails { @@ -3393,7 +3718,7 @@ declare namespace Electron { * usage can be referenced above. You can also attach other fields to the element * of the template and they will become properties of the constructed menu items. */ - static buildFromTemplate(template: MenuItemConstructorOptions[]): Menu; + static buildFromTemplate(template: Array<(MenuItemConstructorOptions) | (MenuItem)>): Menu; /** * Note: The returned Menu instance doesn't support dynamic addition or removal of * menu items. Instance properties can still be dynamically modified. @@ -3408,9 +3733,16 @@ declare namespace Electron { static sendActionToFirstResponder(action: string): void; /** * Sets menu as the application menu on macOS. On Windows and Linux, the menu will - * be set as each window's top menu. Passing null will remove the menu bar on - * Windows and Linux but has no effect on macOS. Note: This API has to be called - * after the ready event of app module. + * be set as each window's top menu. Also on Windows and Linux, you can use a & in + * the top-level item name to indicate which letter should get a generated + * accelerator. For example, using &File for the file menu would result in a + * generated Alt-F accelerator that opens the associated menu. The indicated + * character in the button label gets an underline. The & character is not + * displayed on the button label. Passing null will suppress the default menu. On + * Windows and Linux, this has the additional effect of removing the menu bar from + * the window. Note: The default menu will be created automatically if the app does + * not set one. It contains standard items such as File, Edit, View, Window and + * Help. */ static setApplicationMenu(menu: (Menu) | (null)): void; /** @@ -3438,10 +3770,20 @@ declare namespace Electron { // Docs: http://electronjs.org/docs/api/menu-item constructor(options: MenuItemConstructorOptions); + accelerator: string; checked: boolean; click: Function; + commandId: number; enabled: boolean; + icon: NativeImage; + id: string; label: string; + menu: Menu; + registerAccelerator: boolean; + role: string; + sublabel: string; + submenu: Menu; + type: string; visible: boolean; } @@ -3468,7 +3810,13 @@ declare namespace Electron { */ static createEmpty(): NativeImage; /** - * Creates a new NativeImage instance from buffer. + * Creates a new NativeImage instance from buffer that contains the raw bitmap + * pixel data returned by toBitmap(). The specific format is platform-dependent. + */ + static createFromBitmap(buffer: Buffer, options: CreateFromBitmapOptions): NativeImage; + /** + * Creates a new NativeImage instance from buffer. Tries to decode as PNG or JPEG + * first. */ static createFromBuffer(buffer: Buffer, options?: CreateFromBufferOptions): NativeImage; /** @@ -3556,9 +3904,14 @@ declare namespace Electron { startLogging(path: string): void; /** * Stops recording network events. If not called, net logging will automatically - * end when app quits. + * end when app quits. Deprecated Soon */ stopLogging(callback?: (path: string) => void): void; + /** + * Stops recording network events. If not called, net logging will automatically + * end when app quits. + */ + stopLogging(): Promise; /** * A Boolean property that indicates whether network logs are recorded. */ @@ -3737,6 +4090,15 @@ declare namespace Electron { once(event: 'unlock-screen', listener: Function): this; addListener(event: 'unlock-screen', listener: Function): this; removeListener(event: 'unlock-screen', listener: Function): this; + /** + * Calculate the system idle state. idleThreshold is the amount of time (in + * seconds) before considered idle. locked is available on supported systems only. + */ + getSystemIdleState(idleThreshold: number): ('active' | 'idle' | 'locked' | 'unknown'); + /** + * Calculate system idle time in seconds. + */ + getSystemIdleTime(): number; /** * Calculate the system idle state. idleThreshold is the amount of time (in * seconds) before considered idle. callback will be called synchronously on some @@ -3782,6 +4144,26 @@ declare namespace Electron { status: number; } + interface ProcessMemoryInfo { + + // Docs: http://electronjs.org/docs/api/structures/process-memory-info + + /** + * The amount of memory not shared by other processes, such as JS heap or HTML + * content in Kilobytes. + */ + private: number; + /** + * and The amount of memory currently pinned to actual physical RAM in Kilobytes. + */ + residentSet: number; + /** + * The amount of memory shared between processes, typically memory consumed by the + * Electron code itself in Kilobytes. + */ + shared: number; + } + interface ProcessMetric { // Docs: http://electronjs.org/docs/api/structures/process-metric @@ -3795,9 +4177,9 @@ declare namespace Electron { */ pid: number; /** - * Process type (Browser or Tab or GPU etc). + * Process type. One of the following values: */ - type: string; + type: ('Browser' | 'Tab' | 'Utility' | 'Zygote' | 'GPU' | 'Unknown'); } interface Product { @@ -3812,15 +4194,16 @@ declare namespace Electron { * A string that identifies the version of the content. */ contentVersion: string; - /** - * A Boolean value that indicates whether the App Store has downloadable content - * for this product. - */ - downloadable: boolean; /** * The locale formatted price of the product. */ formattedPrice: string; + /** + * A Boolean value that indicates whether the App Store has downloadable content + * for this product. true if at least one file has been associated with the + * product. + */ + isDownloadable: boolean; /** * A description of the product. */ @@ -3870,9 +4253,10 @@ declare namespace Electron { interceptStringProtocol(scheme: string, handler: (request: InterceptStringProtocolRequest, callback: (data?: string) => void) => void, completion?: (error: Error) => void): void; /** * The callback will be called with a boolean that indicates whether there is - * already a handler for scheme. + * already a handler for scheme. Deprecated Soon */ - isProtocolHandled(scheme: string, callback: (error: Error) => void): void; + isProtocolHandled(scheme: string, callback: (handled: boolean) => void): void; + isProtocolHandled(scheme: string): Promise; /** * Registers a protocol of scheme that will send a Buffer as a response. The usage * is the same with registerFileProtocol, except that the callback should be called @@ -3887,13 +4271,14 @@ declare namespace Electron { * scheme is successfully registered or completion(error) when failed. To handle * the request, the callback should be called with either the file's path or an * object that has a path property, e.g. callback(filePath) or callback({ path: - * filePath }). When callback is called with nothing, a number, or an object that - * has an error property, the request will fail with the error number you - * specified. For the available error numbers you can use, please see the net error - * list. By default the scheme is treated like http:, which is parsed differently - * than protocols that follow the "generic URI syntax" like file:, so you probably - * want to call protocol.registerStandardSchemes to have your scheme treated as a - * standard scheme. + * filePath }). The object may also have a headers property which gives a map of + * headers to values for the response headers, e.g. callback({ path: filePath, + * headers: {"Content-Security-Policy": "default-src 'none'"]}). When callback is + * called with nothing, a number, or an object that has an error property, the + * request will fail with the error number you specified. For the available error + * numbers you can use, please see the net error list. By default the scheme is + * treated like http:, which is parsed differently than protocols that follow the + * "generic URI syntax" like file:. */ registerFileProtocol(scheme: string, handler: (request: RegisterFileProtocolRequest, callback: (filePath?: string) => void) => void, completion?: (error: Error) => void): void; /** @@ -3905,24 +4290,31 @@ declare namespace Electron { * set session to null. For POST requests the uploadData object must be provided. */ registerHttpProtocol(scheme: string, handler: (request: RegisterHttpProtocolRequest, callback: (redirectRequest: RedirectRequest) => void) => void, completion?: (error: Error) => void): void; - registerServiceWorkerSchemes(schemes: string[]): void; /** - * A standard scheme adheres to what RFC 3986 calls generic URI syntax. For example - * http and https are standard schemes, while file is not. Registering a scheme as - * standard, will allow relative and absolute resources to be resolved correctly - * when served. Otherwise the scheme will behave like the file protocol, but - * without the ability to resolve relative URLs. For example when you load - * following page with custom protocol without registering it as standard scheme, - * the image will not be loaded because non-standard schemes can not recognize - * relative URLs: Registering a scheme as standard will allow access to files - * through the FileSystem API. Otherwise the renderer will throw a security error - * for the scheme. By default web storage apis (localStorage, sessionStorage, - * webSQL, indexedDB, cookies) are disabled for non standard schemes. So in general - * if you want to register a custom protocol to replace the http protocol, you have - * to register it as a standard scheme: Note: This method can only be used before - * the ready event of the app module gets emitted. + * Note: This method can only be used before the ready event of the app module gets + * emitted and can be called only once. Registers the scheme as standard, secure, + * bypasses content security policy for resources, allows registering ServiceWorker + * and supports fetch API. Specify a privilege with the value of true to enable the + * capability. An example of registering a privileged scheme, with bypassing + * Content Security Policy: A standard scheme adheres to what RFC 3986 calls + * generic URI syntax. For example http and https are standard schemes, while file + * is not. Registering a scheme as standard, will allow relative and absolute + * resources to be resolved correctly when served. Otherwise the scheme will behave + * like the file protocol, but without the ability to resolve relative URLs. For + * example when you load following page with custom protocol without registering it + * as standard scheme, the image will not be loaded because non-standard schemes + * can not recognize relative URLs: Registering a scheme as standard will allow + * access to files through the FileSystem API. Otherwise the renderer will throw a + * security error for the scheme. By default web storage apis (localStorage, + * sessionStorage, webSQL, indexedDB, cookies) are disabled for non standard + * schemes. So in general if you want to register a custom protocol to replace the + * http protocol, you have to register it as a standard scheme. + * protocol.registerSchemesAsPrivileged can be used to replicate the functionality + * of the previous protocol.registerStandardSchemes, webFrame.registerURLSchemeAs* + * and protocol.registerServiceWorkerSchemes functions that existed prior to + * Electron 5.0.0, for example: before (<= v4.x) after (>= v5.x) */ - registerStandardSchemes(schemes: string[], options?: RegisterStandardSchemesOptions): void; + registerSchemesAsPrivileged(customSchemes: CustomScheme[]): void; /** * Registers a protocol of scheme that will send a Readable as a response. The * usage is similar to the other register{Any}Protocol, except that the callback @@ -4203,22 +4595,33 @@ declare namespace Electron { * authentication. */ allowNTLMCredentialsForDomains(domains: string): void; + clearAuthCache(): Promise; + clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate)): Promise; /** - * Clears the session’s HTTP authentication cache. + * Clears the session’s HTTP authentication cache. Deprecated Soon */ - clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate), callback?: Function): void; + clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate), callback: Function): void; /** * Clears the session’s HTTP cache. */ - clearCache(callback: Function): void; + clearCache(): Promise; + /** + * Clears the session’s HTTP cache. Deprecated Soon + */ + clearCache(callback: (error: number) => void): void; /** * Clears the host resolver cache. */ + clearHostResolverCache(): Promise; + /** + * Clears the host resolver cache. Deprecated Soon + */ clearHostResolverCache(callback?: Function): void; /** - * Clears the data of web storages. + * Clears the storage data for the current session. Deprecated Soon */ clearStorageData(options?: ClearStorageDataOptions, callback?: Function): void; + clearStorageData(options?: ClearStorageDataOptions): Promise; /** * Allows resuming cancelled or interrupted downloads from previous Session. The * API will generate a DownloadItem that can be accessed with the will-download @@ -4240,16 +4643,22 @@ declare namespace Electron { * Writes any unwritten DOMStorage data to disk. */ flushStorageData(): void; - getBlobData(identifier: string, callback: (result: Buffer) => void): void; /** - * Callback is invoked with the session's current cache size. + * Deprecated Soon */ - getCacheSize(callback: (size: number) => void): void; + getBlobData(identifier: string, callback: (result: Buffer) => void): void; + getBlobData(identifier: string): Promise; + getCacheSize(): Promise; + /** + * Callback is invoked with the session's current cache size. Deprecated Soon + */ + getCacheSize(callback: (size: number, error: number) => void): void; getPreloads(): string[]; getUserAgent(): string; + resolveProxy(url: string): Promise; /** * Resolves the proxy information for url. The callback will be called with - * callback(proxy) when the request is performed. + * callback(proxy) when the request is performed. Deprecated Soon */ resolveProxy(url: string, callback: (proxy: string) => void): void; /** @@ -4288,6 +4697,13 @@ declare namespace Electron { * proxyRules has to follow the rules below: For example: The proxyBypassRules is a * comma separated list of rules described below: */ + setProxy(config: Config): Promise; + /** + * Sets the proxy settings. When pacScript and proxyRules are provided together, + * the proxyRules option is ignored and pacScript configuration is applied. The + * proxyRules has to follow the rules below: For example: The proxyBypassRules is a + * comma separated list of rules described below: Deprecated Soon + */ setProxy(config: Config, callback: Function): void; /** * Overrides the userAgent and acceptLanguages for this session. The @@ -4319,7 +4735,12 @@ declare namespace Electron { * Open the given external protocol URL in the desktop's default manner. (For * example, mailto: URLs in the user's default mail agent). */ - openExternal(url: string, options?: OpenExternalOptions, callback?: (error: Error) => void): boolean; + openExternal(url: string, options?: OpenExternalOptions): Promise; + /** + * Open the given external protocol URL in the desktop's default manner. (For + * example, mailto: URLs in the user's default mail agent). Deprecated + */ + openExternalSync(url: string, options?: OpenExternalSyncOptions): boolean; /** * Open the given file in the desktop's default manner. */ @@ -4332,7 +4753,7 @@ declare namespace Electron { /** * Show the given file in a file manager. If possible, select the file. */ - showItemInFolder(fullPath: string): boolean; + showItemInFolder(fullPath: string): void; /** * Creates or updates a shortcut link at shortcutPath. */ @@ -4433,28 +4854,48 @@ declare namespace Electron { once(event: 'color-changed', listener: (event: Event) => void): this; addListener(event: 'color-changed', listener: (event: Event) => void): this; removeListener(event: 'color-changed', listener: (event: Event) => void): this; + on(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; + once(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; + addListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; + removeListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; on(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; once(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; addListener(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; removeListener(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; /** @@ -4467,15 +4908,26 @@ declare namespace Electron { * was not required until macOS 10.14 Mojave, so this method will always return * true if your system is running 10.13 High Sierra or lower. */ - askForMediaAccess(mediaType: 'microphone' | 'camera'): Promise; + askForMediaAccess(mediaType: 'microphone' | 'camera'): Promise; + /** + * NOTE: This API will return false on macOS systems older than Sierra 10.12.2. + */ + canPromptTouchID(): boolean; + /** + * This API is only available on macOS 10.14 Mojave or newer. + */ getAccentColor(): string; + /** + * Returns an object with system animation settings. + */ + getAnimationSettings(): AnimationSettings; /** * Gets the macOS appearance setting that you have declared you want for your * application, maps to NSApplication.appearance. You can use the * setAppLevelAppearance API to set this value. */ getAppLevelAppearance(): ('dark' | 'light' | 'unknown'); - getColor(color: '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text'): string; + getColor(color: '3d-dark-shadow' | '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text' | 'alternate-selected-control-text' | 'alternate-selected-control-text' | 'control-background' | 'control' | 'control-text' | 'disabled-control-text' | 'find-highlight' | 'grid' | 'header-text' | 'highlight' | 'keyboard-focus-indicator' | 'label' | 'link' | 'placeholder-text' | 'quaternary-label' | 'scrubber-textured-background' | 'secondary-label' | 'selected-content-background' | 'selected-control' | 'selected-control-text' | 'selected-menu-item' | 'selected-text-background' | 'selected-text' | 'separator' | 'shadow' | 'tertiary-label' | 'text-background' | 'text' | 'under-page-background' | 'unemphasized-selected-content-background' | 'unemphasized-selected-text-background' | 'unemphasized-selected-text' | 'window-background' | 'window-frame-text'): string; /** * Gets the macOS appearance setting that is currently applied to your application, * maps to NSApplication.effectiveAppearance Please note that until Electron is @@ -4492,6 +4944,12 @@ declare namespace Electron { * always return granted if your system is running 10.13 High Sierra or lower. */ getMediaAccessStatus(mediaType: string): ('not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'); + /** + * Returns one of several standard system colors that automatically adapt to + * vibrancy and changes in accessibility settings like 'Increase contrast' and + * 'Reduce transparency'. See Apple Documentation for more details. + */ + getSystemColor(color: 'blue' | 'brown' | 'gray' | 'green' | 'orange' | 'pink' | 'purple' | 'red' | 'yellow'): void; /** * Some popular key and types are: */ @@ -4502,6 +4960,7 @@ declare namespace Electron { */ isAeroGlassEnabled(): boolean; isDarkMode(): boolean; + isHighContrastColorScheme(): boolean; isInvertedColorScheme(): boolean; isSwipeTrackingFromScrollEventsEnabled(): boolean; isTrustedAccessibilityClient(prompt: boolean): boolean; @@ -4514,12 +4973,22 @@ declare namespace Electron { * Posts event as native notifications of macOS. The userInfo is an Object that * contains the user information dictionary sent along with the notification. */ - postNotification(event: string, userInfo: any): void; + postNotification(event: string, userInfo: any, deliverImmediately?: boolean): void; /** * Posts event as native notifications of macOS. The userInfo is an Object that * contains the user information dictionary sent along with the notification. */ postWorkspaceNotification(event: string, userInfo: any): void; + /** + * This API itself will not protect your user data; rather, it is a mechanism to + * allow you to do so. Native apps will need to set Access Control Constants like + * kSecAccessControlUserPresence on the their keychain entry so that reading it + * would auto-prompt for Touch ID biometric consent. This could be done with + * node-keytar, such that one would store an encryption key with node-keytar and + * only fetch it if promptTouchID() resolves. NOTE: This API will return a rejected + * Promise on macOS systems older than Sierra 10.12.2. + */ + promptTouchID(reason: string): Promise; /** * Add the specified defaults to your application's NSUserDefaults. */ @@ -4608,6 +5077,10 @@ declare namespace Electron { * The string to be displayed in a JumpList. */ title: string; + /** + * The working directory. Default is empty. + */ + workingDirectory?: string; } interface ThumbarButton { @@ -4829,7 +5302,7 @@ declare namespace Electron { /** * Emitted when the tray icon is clicked. */ - on(event: 'click', listener: (event: Event, + on(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4838,7 +5311,7 @@ declare namespace Electron { * The position of the event. */ position: Point) => void): this; - once(event: 'click', listener: (event: Event, + once(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4847,7 +5320,7 @@ declare namespace Electron { * The position of the event. */ position: Point) => void): this; - addListener(event: 'click', listener: (event: Event, + addListener(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4856,7 +5329,7 @@ declare namespace Electron { * The position of the event. */ position: Point) => void): this; - removeListener(event: 'click', listener: (event: Event, + removeListener(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4868,22 +5341,22 @@ declare namespace Electron { /** * Emitted when the tray icon is double clicked. */ - on(event: 'double-click', listener: (event: Event, + on(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - once(event: 'double-click', listener: (event: Event, + once(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - addListener(event: 'double-click', listener: (event: Event, + addListener(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - removeListener(event: 'double-click', listener: (event: Event, + removeListener(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4965,22 +5438,22 @@ declare namespace Electron { /** * Emitted when the mouse enters the tray icon. */ - on(event: 'mouse-enter', listener: (event: Event, + on(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - once(event: 'mouse-enter', listener: (event: Event, + once(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - addListener(event: 'mouse-enter', listener: (event: Event, + addListener(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - removeListener(event: 'mouse-enter', listener: (event: Event, + removeListener(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ @@ -4988,22 +5461,22 @@ declare namespace Electron { /** * Emitted when the mouse exits the tray icon. */ - on(event: 'mouse-leave', listener: (event: Event, + on(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - once(event: 'mouse-leave', listener: (event: Event, + once(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - addListener(event: 'mouse-leave', listener: (event: Event, + addListener(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - removeListener(event: 'mouse-leave', listener: (event: Event, + removeListener(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ @@ -5011,22 +5484,22 @@ declare namespace Electron { /** * Emitted when the mouse moves in the tray icon. */ - on(event: 'mouse-move', listener: (event: Event, + on(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - once(event: 'mouse-move', listener: (event: Event, + once(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - addListener(event: 'mouse-move', listener: (event: Event, + addListener(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - removeListener(event: 'mouse-move', listener: (event: Event, + removeListener(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ @@ -5034,22 +5507,22 @@ declare namespace Electron { /** * Emitted when the tray icon is right clicked. */ - on(event: 'right-click', listener: (event: Event, + on(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - once(event: 'right-click', listener: (event: Event, + once(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - addListener(event: 'right-click', listener: (event: Event, + addListener(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - removeListener(event: 'right-click', listener: (event: Event, + removeListener(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -5068,6 +5541,7 @@ declare namespace Electron { */ getBounds(): Rectangle; getIgnoreDoubleClickEvents(): boolean; + getTitle(title: string): string; isDestroyed(): boolean; /** * Pops up the context menu of the tray icon. When menu is passed, the menu will be @@ -5080,9 +5554,9 @@ declare namespace Electron { */ setContextMenu(menu: (Menu) | (null)): void; /** - * Sets when the tray's icon background becomes highlighted (in blue). Note: You - * can use highlightMode with a BrowserWindow by toggling between 'never' and - * 'always' modes when the window visibility changes. + * Sets when the tray's icon background becomes highlighted (in blue). Deprecated + * Note: You can use highlightMode with a BrowserWindow by toggling between 'never' + * and 'always' modes when the window visibility changes. */ setHighlightMode(mode: 'selection' | 'always' | 'never'): void; /** @@ -5100,7 +5574,7 @@ declare namespace Electron { */ setPressedImage(image: (NativeImage) | (string)): void; /** - * Sets the title displayed aside of the tray icon in the status bar (Support ANSI + * Sets the title displayed next to the tray icon in the status bar (Support ANSI * colors). */ setTitle(title: string): void; @@ -5368,6 +5842,14 @@ declare namespace Electron { * coordinates of the custom cursor's hotspot. */ hotspot?: Point) => void): this; + /** + * Emitted when desktopCapturer.getSources() is called in the renderer process. + * Calling event.preventDefault() will make it return empty sources. + */ + on(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; + once(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; + addListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; + removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; /** * Emitted when webContents is destroyed. */ @@ -5716,6 +6198,13 @@ declare namespace Electron { once(event: 'dom-ready', listener: (event: Event) => void): this; addListener(event: 'dom-ready', listener: (event: Event) => void): this; removeListener(event: 'dom-ready', listener: (event: Event) => void): this; + /** + * Emitted when the window enters a full-screen state triggered by HTML API. + */ + on(event: 'enter-html-full-screen', listener: Function): this; + once(event: 'enter-html-full-screen', listener: Function): this; + addListener(event: 'enter-html-full-screen', listener: Function): this; + removeListener(event: 'enter-html-full-screen', listener: Function): this; /** * Emitted when a result is available for [webContents.findInPage] request. */ @@ -5727,6 +6216,45 @@ declare namespace Electron { result: Result) => void): this; removeListener(event: 'found-in-page', listener: (event: Event, result: Result) => void): this; + /** + * Emitted when the renderer process sends an asynchronous message via + * ipcRenderer.send(). + */ + on(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + once(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + addListener(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + removeListener(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + /** + * Emitted when the renderer process sends a synchronous message via + * ipcRenderer.sendSync(). + */ + on(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + once(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + addListener(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + removeListener(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + /** + * Emitted when the window leaves a full-screen state triggered by HTML API. + */ + on(event: 'leave-html-full-screen', listener: Function): this; + once(event: 'leave-html-full-screen', listener: Function): this; + addListener(event: 'leave-html-full-screen', listener: Function): this; + removeListener(event: 'leave-html-full-screen', listener: Function): this; /** * Emitted when webContents wants to do basic auth. The usage is the same with the * login event of app. @@ -5941,6 +6469,21 @@ declare namespace Electron { removeListener(event: 'plugin-crashed', listener: (event: Event, name: string, version: string) => void): this; + /** + * Emitted when the preload script preloadPath throws an unhandled exception error. + */ + on(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; + once(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; + addListener(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; + removeListener(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; /** * Emitted when remote.getBuiltin() is called in the renderer process. Calling * event.preventDefault() will prevent the module from being returned. Custom value @@ -6205,16 +6748,23 @@ declare namespace Electron { canGoBack(): boolean; canGoForward(): boolean; canGoToOffset(offset: number): boolean; + /** + * Captures a snapshot of the page within rect. Omitting rect will capture the + * whole visible page. + */ + capturePage(rect?: Rectangle): Promise; /** * Captures a snapshot of the page within rect. Upon completion callback will be * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; /** * Captures a snapshot of the page within rect. Upon completion callback will be * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(callback: (image: NativeImage) => void): void; /** @@ -6261,12 +6811,15 @@ declare namespace Electron { /** * Evaluates code in page. In the browser window some HTML APIs like * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. If the result of the executed - * code is a promise the callback result will be the resolved value of the promise. - * We recommend that you use the returned Promise to handle code that results in a - * Promise. + * userGesture to true will remove this limitation. Deprecated Soon */ executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; + /** + * Evaluates code in page. In the browser window some HTML APIs like + * requestFullScreen can only be invoked by a gesture from the user. Setting + * userGesture to true will remove this limitation. + */ + executeJavaScript(code: string, userGesture?: boolean): Promise; /** * Starts a request to find all matches for the text in the web page. The result of * the request can be obtained by subscribing to found-in-page event. @@ -6288,16 +6841,8 @@ declare namespace Electron { getURL(): string; getUserAgent(): string; getWebRTCIPHandlingPolicy(): string; - /** - * Sends a request to get current zoom factor, the callback will be called with - * callback(zoomFactor). - */ - getZoomFactor(callback: (zoomFactor: number) => void): void; - /** - * Sends a request to get current zoom level, the callback will be called with - * callback(zoomLevel). - */ - getZoomLevel(callback: (zoomLevel: number) => void): void; + getZoomFactor(): number; + getZoomLevel(): number; /** * Makes the browser go back a web page. */ @@ -6314,11 +6859,6 @@ declare namespace Electron { * Navigates to the specified offset from the "current entry". */ goToOffset(offset: number): void; - /** - * Checks if any ServiceWorker is registered and returns a boolean as response to - * callback. - */ - hasServiceWorker(callback: (hasWorker: boolean) => void): void; /** * Injects CSS into the current web page. */ @@ -6335,6 +6875,10 @@ declare namespace Electron { * Opens the developer tools for the service worker context. */ inspectServiceWorker(): void; + /** + * Opens the developer tools for the shared worker context. + */ + inspectSharedWorker(): void; /** * Schedules a full repaint of the window this web contents is in. If offscreen * rendering is enabled invalidates the frame and generates a new one through the @@ -6358,13 +6902,13 @@ declare namespace Electron { * relative to the root of your application. For instance an app structure like * this: Would require code like this */ - loadFile(filePath: string, options?: LoadFileOptions): void; + loadFile(filePath: string, options?: LoadFileOptions): Promise; /** * Loads the url in the window. The url must contain the protocol prefix, e.g. the * http:// or file://. If the load should bypass http cache then use the pragma * header to achieve it. */ - loadURL(url: string, options?: LoadURLOptions): void; + loadURL(url: string, options?: LoadURLOptions): Promise; /** * Opens the devtools. When contents is a tag, the mode would be detach * by default, explicitly passing an empty mode can force using last used dock @@ -6387,13 +6931,18 @@ declare namespace Electron { * Use page-break-before: always; CSS style to force to print to a new page. */ print(options?: PrintOptions, callback?: (success: boolean) => void): void; + /** + * Prints window's web page as PDF with Chromium's preview printing custom + * settings. The landscape will be ignored if @page CSS at-rule is used in the web + * page. By default, an empty options will be regarded as: Use page-break-before: + * always; CSS style to force to print to a new page. An example of + * webContents.printToPDF: + */ + printToPDF(options: PrintToPDFOptions): Promise; /** * Prints window's web page as PDF with Chromium's preview printing custom * settings. The callback will be called with callback(error, data) on completion. - * The data is a Buffer that contains the generated PDF data. The landscape will be - * ignored if @page CSS at-rule is used in the web page. By default, an empty - * options will be regarded as: Use page-break-before: always; CSS style to force - * to print to a new page. An example of webContents.printToPDF: + * The data is a Buffer that contains the generated PDF data. Deprecated Soon */ printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; /** @@ -6420,7 +6969,7 @@ declare namespace Electron { * Executes the editing command replaceMisspelling in web page. */ replaceMisspelling(text: string): void; - savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML', callback: (error: Error) => void): boolean; + savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML'): Promise; /** * Executes the editing command selectAll in web page. */ @@ -6441,6 +6990,16 @@ declare namespace Electron { * object also have following properties: */ sendInputEvent(event: Event): void; + /** + * Send an asynchronous message to a specific frame in a renderer process via + * channel. Arguments will be serialized as JSON internally and as such no + * functions or prototype chains will be included. The renderer process can handle + * the message by listening to channel with the ipcRenderer module. If you want to + * get the frameId of a given renderer context you should use the + * webFrame.routingId value. E.g. You can also read frameId from all incoming IPC + * messages in the main process. + */ + sendToFrame(frameId: number, channel: string, ...args: any[]): void; /** * Mute the audio on the current web page. */ @@ -6539,12 +7098,6 @@ declare namespace Electron { * Executes the editing command undo in web page. */ undo(): void; - /** - * Unregisters any ServiceWorker if present and returns a boolean as response to - * callback when the JS promise is fulfilled or false when the JS promise is - * rejected. - */ - unregisterServiceWorker(callback: (success: boolean) => void): void; /** * Executes the editing command unselect in web page. */ @@ -6574,11 +7127,22 @@ declare namespace Electron { * requestFullScreen can only be invoked by a gesture from the user. Setting * userGesture to true will remove this limitation. */ + executeJavaScript(code: string, userGesture?: boolean): Promise; + /** + * Evaluates code in page. In the browser window some HTML APIs like + * requestFullScreen can only be invoked by a gesture from the user. Setting + * userGesture to true will remove this limitation. Deprecated Soon + */ executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; /** - * Work like executeJavaScript but evaluates scripts in an isolated context. + * Works like executeJavaScript but evaluates scripts in an isolated context. + * Deprecated Soon */ - executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): void; + executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): Promise; + /** + * Works like executeJavaScript but evaluates scripts in an isolated context. + */ + executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean): Promise; findFrameByName(name: string): WebFrame; findFrameByRoutingId(routingId: number): WebFrame; getFrameForSelector(selector: string): WebFrame; @@ -6589,22 +7153,14 @@ declare namespace Electron { getResourceUsage(): ResourceUsage; getZoomFactor(): number; getZoomLevel(): number; + /** + * Inserts css as a style sheet in the document. + */ + insertCSS(css: string): void; /** * Inserts text to the focused element. */ insertText(text: string): void; - /** - * Resources will be loaded from this scheme regardless of the current page's - * Content Security Policy. - */ - registerURLSchemeAsBypassingCSP(scheme: string): void; - /** - * Registers the scheme as secure, bypasses content security policy for resources, - * allows registering ServiceWorker and supports fetch API. Specify an option with - * the value of false to omit it from the registration. An example of registering a - * privileged scheme, without bypassing Content Security Policy: - */ - registerURLSchemeAsPrivileged(scheme: string, options?: RegisterURLSchemeAsPrivilegedOptions): void; /** * Set the content security policy of the isolated world. */ @@ -6613,6 +7169,11 @@ declare namespace Electron { * Set the name of the isolated world. Useful in devtools. */ setIsolatedWorldHumanReadableName(worldId: number, name: string): void; + /** + * Set the security origin, content security policy and name of the isolated world. + * Note: If the csp is specified, then the securityOrigin also has to be specified. + */ + setIsolatedWorldInfo(worldId: number, info: Info): void; /** * Set the security origin of the isolated world. */ @@ -6623,10 +7184,12 @@ declare namespace Electron { setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; /** * Sets a provider for spell checking in input fields and text areas. The provider - * must be an object that has a spellCheck method that returns whether the word - * passed is correctly spelled. An example of using node-spellchecker as provider: + * must be an object that has a spellCheck method that accepts an array of + * individual words for spellchecking. The spellCheck function runs asynchronously + * and calls the callback function with an array of misspelt words when complete. + * An example of using node-spellchecker as provider: */ - setSpellCheckProvider(language: string, autoCorrectWord: boolean, provider: Provider): void; + setSpellCheckProvider(language: string, provider: Provider): void; /** * Sets the maximum and minimum pinch-to-zoom level. */ @@ -6685,90 +7248,90 @@ declare namespace Electron { * The listener will be called with listener(details) when a server initiated * redirect is about to occur. */ - onBeforeRedirect(listener: (details: OnBeforeRedirectDetails) => void): void; + onBeforeRedirect(listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when a server initiated * redirect is about to occur. */ - onBeforeRedirect(filter: OnBeforeRedirectFilter, listener: (details: OnBeforeRedirectDetails) => void): void; + onBeforeRedirect(filter: OnBeforeRedirectFilter, listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when a request is * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. + * has to be called with an response object. Some examples of valid urls: */ - onBeforeRequest(listener: (details: OnBeforeRequestDetails, callback: (response: Response) => void) => void): void; + onBeforeRequest(listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when a request is * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. + * has to be called with an response object. Some examples of valid urls: */ - onBeforeRequest(filter: OnBeforeRequestFilter, listener: (details: OnBeforeRequestDetails, callback: (response: Response) => void) => void): void; + onBeforeRequest(filter: OnBeforeRequestFilter, listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) before sending an * HTTP request, once the request headers are available. This may occur after a TCP * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; + onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) before sending an * HTTP request, once the request headers are available. This may occur after a TCP * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; + onBeforeSendHeaders(listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details) when a request is completed. */ - onCompleted(filter: OnCompletedFilter, listener: (details: OnCompletedDetails) => void): void; + onCompleted(filter: OnCompletedFilter, listener: ((details: OnCompletedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when a request is completed. */ - onCompleted(listener: (details: OnCompletedDetails) => void): void; + onCompleted(listener: ((details: OnCompletedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when an error occurs. */ - onErrorOccurred(listener: (details: OnErrorOccurredDetails) => void): void; + onErrorOccurred(listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when an error occurs. */ - onErrorOccurred(filter: OnErrorOccurredFilter, listener: (details: OnErrorOccurredDetails) => void): void; + onErrorOccurred(filter: OnErrorOccurredFilter, listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when HTTP response * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(filter: OnHeadersReceivedFilter, listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; + onHeadersReceived(filter: OnHeadersReceivedFilter, listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when HTTP response * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; + onHeadersReceived(listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details) when first byte of the * response body is received. For HTTP requests, this means that the status line * and response headers are available. */ - onResponseStarted(listener: (details: OnResponseStartedDetails) => void): void; + onResponseStarted(listener: ((details: OnResponseStartedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when first byte of the * response body is received. For HTTP requests, this means that the status line * and response headers are available. */ - onResponseStarted(filter: OnResponseStartedFilter, listener: (details: OnResponseStartedDetails) => void): void; + onResponseStarted(filter: OnResponseStartedFilter, listener: ((details: OnResponseStartedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) just before a request is * going to be sent to the server, modifications of previous onBeforeSendHeaders * response are visible by the time this listener is fired. */ - onSendHeaders(filter: OnSendHeadersFilter, listener: (details: OnSendHeadersDetails) => void): void; + onSendHeaders(filter: OnSendHeadersFilter, listener: ((details: OnSendHeadersDetails) => void) | (null)): void; /** * The listener will be called with listener(details) just before a request is * going to be sent to the server, modifications of previous onBeforeSendHeaders * response are visible by the time this listener is fired. */ - onSendHeaders(listener: (details: OnSendHeadersDetails) => void): void; + onSendHeaders(listener: ((details: OnSendHeadersDetails) => void) | (null)): void; } interface WebSource { @@ -6963,15 +7526,24 @@ declare namespace Electron { canGoForward(): boolean; canGoToOffset(offset: number): boolean; /** - * Captures a snapshot of the webview's page. Same as - * webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(callback: (image: NativeImage) => void): void; /** - * Captures a snapshot of the webview's page. Same as - * webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * Captures a snapshot of the page within rect. Omitting rect will capture the + * whole visible page. + */ + capturePage(rect?: Rectangle): Promise; /** * Clears the navigation history. */ @@ -6996,12 +7568,18 @@ declare namespace Electron { * Initiates a download of the resource at url without navigating. */ downloadURL(url: string): void; + /** + * Evaluates code in page. If userGesture is set, it will create the user gesture + * context in the page. HTML APIs like requestFullScreen, which require user + * action, can take advantage of this option for automation. Deprecated Soon + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; /** * Evaluates code in page. If userGesture is set, it will create the user gesture * context in the page. HTML APIs like requestFullScreen, which require user * action, can take advantage of this option for automation. */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): void; + executeJavaScript(code: string, userGesture?: boolean): Promise; /** * Starts a request to find all matches for the text in the web page. The result of * the request can be obtained by subscribing to found-in-page event. @@ -7015,16 +7593,9 @@ declare namespace Electron { * is disabled. */ getWebContents(): WebContents; - /** - * Sends a request to get current zoom factor, the callback will be called with - * callback(zoomFactor). - */ - getZoomFactor(callback: (zoomFactor: number) => void): void; - /** - * Sends a request to get current zoom level, the callback will be called with - * callback(zoomLevel). - */ - getZoomLevel(callback: (zoomLevel: number) => void): void; + getWebContentsId(): number; + getZoomFactor(): number; + getZoomLevel(): number; /** * Makes the guest page go back. */ @@ -7057,6 +7628,10 @@ declare namespace Electron { * Opens the DevTools for the service worker context present in the guest page. */ inspectServiceWorker(): void; + /** + * Opens the DevTools for the shared worker context present in the guest page. + */ + inspectSharedWorker(): void; isAudioMuted(): boolean; isCrashed(): boolean; isCurrentlyAudible(): boolean; @@ -7069,7 +7644,7 @@ declare namespace Electron { * Loads the url in the webview, the url must contain the protocol prefix, e.g. the * http:// or file://. */ - loadURL(url: string, options?: LoadURLOptions): void; + loadURL(url: string, options?: LoadURLOptions): Promise; /** * Opens a DevTools window for guest page. */ @@ -7088,9 +7663,13 @@ declare namespace Electron { print(options?: PrintOptions): void; /** * Prints webview's web page as PDF, Same as webContents.printToPDF(options, - * callback). + * callback). Deprecated Soon */ printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; + /** + * Prints webview's web page as PDF, Same as webContents.printToPDF(options). + */ + printToPDF(options: PrintToPDFOptions): Promise; /** * Executes editing command redo in page. */ @@ -7180,14 +7759,6 @@ declare namespace Electron { * windows. Popups are disabled by default. */ // allowpopups?: string; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * When this attribute is present the webview container will automatically resize - * within the bounds specified by the attributes minwidth, minheight, maxwidth, and - * maxheight. These constraints do not impact the webview unless autosize is - * enabled. When autosize is enabled, the webview container size cannot be less - * than the minimum values or greater than the maximum. - */ - autosize?: string; /** * A list of strings which specifies the blink features to be disabled separated by * ,. The full list of supported feature strings can be found in the @@ -7207,7 +7778,7 @@ declare namespace Electron { enableblinkfeatures?: string; /** * When this attribute is false the guest page in webview will not have access to - * the remote module. The remote module is avaiable by default. + * the remote module. The remote module is available by default. */ enableremotemodule?: string; /** @@ -7220,6 +7791,13 @@ declare namespace Electron { * system resources. Node integration is disabled by default in the guest page. */ nodeintegration?: string; + /** + * Experimental option for enabling NodeJS support in sub-frames such as iframes + * inside the webview. All your preloads will load for every iframe, you can use + * process.isMainFrame to determine if you are in the main frame or not. This + * option is disabled by default in the guest page. + */ + nodeintegrationinsubframes?: string; /** * Sets the session used by the page. If partition starts with persist:, the page * will use a persistent session available to all pages in the app with the same @@ -7281,14 +7859,23 @@ declare namespace Electron { * Copyright information. */ copyright?: string; + /** + * The app's build version number. + */ + version?: string; /** * Credit information. */ credits?: string; /** - * The app's build version number. + * The app's website. */ - version?: string; + website?: string; + /** + * Path to the app's icon. Will be shown as 64x64 pixels while retaining aspect + * ratio. + */ + iconPath?: string; } interface AddRepresentationOptions { @@ -7314,6 +7901,24 @@ declare namespace Electron { dataURL?: string; } + interface AnimationSettings { + /** + * Returns true if rich animations should be rendered. Looks at session type (e.g. + * remote desktop) and accessibility settings to give guidance for heavy + * animations. + */ + shouldRenderRichAnimation: boolean; + /** + * Determines on a per-platform basis whether scroll animations (e.g. produced by + * home/end key) should be enabled. + */ + scrollAnimationsEnabledBySystem: boolean; + /** + * Determines whether the user desires reduced motion based on platform APIs. + */ + prefersReducedMotion: boolean; + } + interface AppDetailsOptions { /** * Window's . It has to be set, otherwise the other options will have no effect. @@ -7357,6 +7962,16 @@ declare namespace Electron { * by default. */ height: boolean; + /** + * If true, the view's x position and width will grow and shrink proportionly with + * the window. false by default. + */ + horizontal: boolean; + /** + * If true, the view's y position and height will grow and shrink proportinaly with + * the window. false by default. + */ + vertical: boolean; } interface BitmapOptions { @@ -7475,7 +8090,9 @@ declare namespace Electron { */ kiosk?: boolean; /** - * Default window title. Default is "Electron". + * Default window title. Default is "Electron". If the HTML tag is defined + * in the HTML file loaded by loadURL(), this property will be + * ignored. */ title?: string; /** @@ -7520,8 +8137,8 @@ declare namespace Electron { enableLargerThanScreen?: boolean; /** * Window's background color as a hexadecimal value, like #66CD00 or #FFF or - * #80FFFFFF (alpha is supported if transparent is set to true). Default is #FFF - * (white). + * #80FFFFFF (alpha in #AARRGGBB format is supported if transparent is set to + * true). Default is #FFF (white). */ backgroundColor?: string; /** @@ -7633,15 +8250,24 @@ declare namespace Electron { interface CommandLine { /** * Append a switch (with optional value) to Chromium's command line. Note: This - * will not affect process.argv, and is mainly used by developers to control some - * low-level Chromium behaviors. + * will not affect process.argv. The intended usage of this function is to control + * Chromium's behavior. */ appendSwitch: (the_switch: string, value?: string) => void; /** * Append an argument to Chromium's command line. The argument will be quoted - * correctly. Note: This will not affect process.argv. + * correctly. Switches will precede arguments regardless of appending order. If + * you're appending an argument like --switch=value, consider using + * appendSwitch('switch', 'value') instead. Note: This will not affect + * process.argv. The intended usage of this function is to control Chromium's + * behavior. */ appendArgument: (value: string) => void; + hasSwitch: (the_switch: string) => boolean; + /** + * Note: When the switch is not present or has no value, it returns empty string. + */ + getSwitchValue: (the_switch: string) => string; } interface Config { @@ -7778,6 +8404,15 @@ declare namespace Electron { crashesDirectory?: string; } + interface CreateFromBitmapOptions { + width: number; + height: number; + /** + * Defaults to 1.0. + */ + scaleFactor?: number; + } + interface CreateFromBufferOptions { /** * Required for bitmap buffers. @@ -7831,7 +8466,7 @@ declare namespace Electron { image?: NativeImage; rtf?: string; /** - * The title of the url at text. + * The title of the URL at text. */ bookmark?: string; } @@ -7850,8 +8485,7 @@ declare namespace Electron { */ value?: string; /** - * The domain of the cookie; this will be normalized with a preceding dot so that - * it's also valid for subdomains. Empty by default if omitted. + * The domain of the cookie. Empty by default if omitted. */ domain?: string; /** @@ -7915,7 +8549,9 @@ declare namespace Electron { * When critical is passed, the dock icon will bounce until either the application * becomes active or the request is canceled. When informational is passed, the * dock icon will bounce for one second. However, the request remains active until - * either the application becomes active or the request is canceled. + * either the application becomes active or the request is canceled. Nota Bene: + * This method can only be used while the app is not focused; when the app is + * focused it will return -1. */ bounce: (type?: 'critical' | 'informational') => number; /** @@ -7935,15 +8571,13 @@ declare namespace Electron { * Hides the dock icon. */ hide: () => void; - /** - * Shows the dock icon. - */ - show: () => void; + show: () => Promise; isVisible: () => boolean; /** * Sets the application's dock menu. */ setMenu: (menu: Menu) => void; + getMenu: () => (Menu) | (null); /** * Sets the image associated with this dock icon. */ @@ -8094,6 +8728,21 @@ declare namespace Electron { password: string; } + interface Info { + /** + * Security origin for the isolated world. + */ + securityOrigin?: string; + /** + * Content Security Policy for the isolated world. + */ + csp?: string; + /** + * Name for isolated world. Useful in devtools. + */ + name?: string; + } + interface Input { /** * Either keyUp or keyDown. @@ -8288,12 +8937,18 @@ declare namespace Electron { * Will be called with click(menuItem, browserWindow, event) when the menu item is * clicked. */ - click?: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event) => void; + click?: (menuItem: MenuItem, browserWindow: BrowserWindow, event: KeyboardEvent) => void; /** - * Define the action of the menu item, when specified the click property will be - * ignored. See . + * Can be undo, redo, cut, copy, paste, pasteAndMatchStyle, delete, selectAll, + * reload, forceReload, toggleDevTools, resetZoom, zoomIn, zoomOut, + * togglefullscreen, window, minimize, close, help, about, services, hide, + * hideOthers, unhide, quit, startSpeaking, stopSpeaking, close, minimize, zoom, + * front, appMenu, fileMenu, editMenu, viewMenu, recentDocuments, toggleTabBar, + * selectNextTab, selectPreviousTab, mergeAllWindows, clearRecentDocuments, + * moveTabToNewWindow or windowMenu Define the action of the menu item, when + * specified the click property will be ignored. See . */ - role?: string; + role?: ('undo' | 'redo' | 'cut' | 'copy' | 'paste' | 'pasteAndMatchStyle' | 'delete' | 'selectAll' | 'reload' | 'forceReload' | 'toggleDevTools' | 'resetZoom' | 'zoomIn' | 'zoomOut' | 'togglefullscreen' | 'window' | 'minimize' | 'close' | 'help' | 'about' | 'services' | 'hide' | 'hideOthers' | 'unhide' | 'quit' | 'startSpeaking' | 'stopSpeaking' | 'close' | 'minimize' | 'zoom' | 'front' | 'appMenu' | 'fileMenu' | 'editMenu' | 'viewMenu' | 'recentDocuments' | 'toggleTabBar' | 'selectNextTab' | 'selectPreviousTab' | 'mergeAllWindows' | 'clearRecentDocuments' | 'moveTabToNewWindow' | 'windowMenu'); /** * Can be normal, separator, submenu, checkbox or radio. */ @@ -8306,6 +8961,11 @@ declare namespace Electron { * If false, the menu item will be greyed out and unclickable. */ enabled?: boolean; + /** + * default is true, and when false will prevent the accelerator from triggering the + * item if the item is not visible`. + */ + acceleratorWorksWhenHidden?: boolean; /** * If false, the menu item will be entirely hidden. */ @@ -8421,6 +9081,82 @@ declare namespace Electron { normalizeAccessKeys?: boolean; } + interface MessageBoxReturnValue { + /** + * The index of the clicked button. + */ + response: number; + /** + * The checked state of the checkbox if checkboxLabel was set. Otherwise false. + */ + checkboxChecked: boolean; + } + + interface MessageBoxSyncOptions { + /** + * Can be "none", "info", "error", "question" or "warning". On Windows, "question" + * displays the same icon as "info", unless you set an icon using the "icon" + * option. On macOS, both "warning" and "error" display the same warning icon. + */ + type?: string; + /** + * Array of texts for buttons. On Windows, an empty array will result in one button + * labeled "OK". + */ + buttons?: string[]; + /** + * Index of the button in the buttons array which will be selected by default when + * the message box opens. + */ + defaultId?: number; + /** + * Title of the message box, some platforms will not show it. + */ + title?: string; + /** + * Content of the message box. + */ + message: string; + /** + * Extra information of the message. + */ + detail?: string; + /** + * If provided, the message box will include a checkbox with the given label. The + * checkbox state can be inspected only when using callback. + */ + checkboxLabel?: string; + /** + * Initial checked state of the checkbox. false by default. + */ + checkboxChecked?: boolean; + icon?: (NativeImage) | (string); + /** + * The index of the button to be used to cancel the dialog, via the Esc key. By + * default this is assigned to the first button with "cancel" or "no" as the label. + * If no such labeled buttons exist and this option is not set, 0 will be used as + * the return value or callback response. + */ + cancelId?: number; + /** + * On Windows Electron will try to figure out which one of the buttons are common + * buttons (like "Cancel" or "Yes"), and show the others as command links in the + * dialog. This can make the dialog appear in the style of modern Windows apps. If + * you don't like this behavior, you can set noLink to true. + */ + noLink?: boolean; + /** + * Normalize the keyboard access keys across platforms. Default is false. Enabling + * this assumes & is used in the button labels for the placement of the keyboard + * shortcut access key and labels will be converted so they work correctly on each + * platform, & characters are removed on macOS, converted to _ on Linux, and left + * untouched on Windows. For example, a button label of Vie&w will be converted to + * Vie_w on Linux and View on macOS and can be selected via Alt-W on Windows and + * Linux. + */ + normalizeAccessKeys?: boolean; + } + interface NewWindowEvent extends Event { url: string; frameName: string; @@ -8488,6 +9224,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; redirectURL: string; statusCode: number; @@ -8513,6 +9250,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; uploadData: UploadData[]; } @@ -8531,6 +9269,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; requestHeaders: RequestHeaders; } @@ -8579,6 +9318,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; fromCache: boolean; /** @@ -8601,6 +9341,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; statusLine: string; statusCode: number; @@ -8634,6 +9375,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; responseHeaders: ResponseHeaders; /** @@ -8658,6 +9400,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; requestHeaders: RequestHeaders; } @@ -8677,6 +9420,11 @@ declare namespace Electron { * back. In detach mode it's not. */ mode: ('right' | 'bottom' | 'undocked' | 'detach'); + /** + * Whether to bring the opened devtools window to the foreground. The default is + * true. + */ + activate?: boolean; } interface OpenDialogOptions { @@ -8703,6 +9451,48 @@ declare namespace Electron { securityScopedBookmarks?: boolean; } + interface OpenDialogReturnValue { + /** + * whether or not the dialog was canceled. + */ + canceled: boolean; + /** + * An array of file paths chosen by the user. If the dialog is cancelled this will + * be an empty array. + */ + filePaths?: string[]; + /** + * An array matching the filePaths array of base64 encoded strings which contains + * security scoped bookmark data. securityScopedBookmarks must be enabled for this + * to be populated. + */ + bookmarks?: string[]; + } + + interface OpenDialogSyncOptions { + title?: string; + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will + * be used. + */ + buttonLabel?: string; + filters?: FileFilter[]; + /** + * Contains which features the dialog should use. The following values are + * supported: + */ + properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'>; + /** + * Message to display above input boxes. + */ + message?: string; + /** + * Create when packaged for the Mac App Store. + */ + securityScopedBookmarks?: boolean; + } + interface OpenExternalOptions { /** * true to bring the opened application to the foreground. The default is true. @@ -8714,6 +9504,17 @@ declare namespace Electron { workingDirectory?: string; } + interface OpenExternalSyncOptions { + /** + * true to bring the opened application to the foreground. The default is true. + */ + activate?: boolean; + /** + * The working directory. + */ + workingDirectory?: string; + } + interface PageFaviconUpdatedEvent extends Event { /** * Array of URLs. @@ -8874,21 +9675,31 @@ declare namespace Electron { landscape?: boolean; } - interface ProcessMemoryInfo { + interface Privileges { /** - * and The amount of memory currently pinned to actual physical RAM in Kilobytes. + * Default false. */ - residentSet: number; + standard?: boolean; /** - * The amount of memory not shared by other processes, such as JS heap or HTML - * content in Kilobytes. + * Default false. */ - private: number; + secure?: boolean; /** - * The amount of memory shared between processes, typically memory consumed by the - * Electron code itself in Kilobytes. + * Default false. */ - shared: number; + bypassCSP?: boolean; + /** + * Default false. + */ + allowServiceWorkers?: boolean; + /** + * Default false. + */ + supportFetchAPI?: boolean; + /** + * Default false. + */ + corsEnabled?: boolean; } interface ProgressBarOptions { @@ -8900,9 +9711,9 @@ declare namespace Electron { interface Provider { /** - * Returns Boolean. + * . */ - spellCheck: (text: string) => void; + spellCheck: (words: string[], callback: (misspeltWords: string[]) => void) => void; } interface ReadBookmark { @@ -8939,13 +9750,6 @@ declare namespace Electron { uploadData: UploadData[]; } - interface RegisterStandardSchemesOptions { - /** - * true to register the scheme as secure. Default false. - */ - secure?: boolean; - } - interface RegisterStreamProtocolRequest { url: string; headers: Headers; @@ -8961,29 +9765,6 @@ declare namespace Electron { uploadData: UploadData[]; } - interface RegisterURLSchemeAsPrivilegedOptions { - /** - * Default true. - */ - secure?: boolean; - /** - * Default true. - */ - bypassCSP?: boolean; - /** - * Default true. - */ - allowServiceWorkers?: boolean; - /** - * Default true. - */ - supportFetchAPI?: boolean; - /** - * Default true. - */ - corsEnabled?: boolean; - } - interface RelaunchOptions { args?: string[]; execPath?: string; @@ -9080,6 +9861,53 @@ declare namespace Electron { securityScopedBookmarks?: boolean; } + interface SaveDialogReturnValue { + /** + * whether or not the dialog was canceled. + */ + canceled: boolean; + /** + * If the dialog is canceled this will be undefined. + */ + filePath?: string; + /** + * Base64 encoded string which contains the security scoped bookmark data for the + * saved file. securityScopedBookmarks must be enabled for this to be present. + */ + bookmark?: string; + } + + interface SaveDialogSyncOptions { + title?: string; + /** + * Absolute directory path, absolute file path, or file name to use by default. + */ + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will + * be used. + */ + buttonLabel?: string; + filters?: FileFilter[]; + /** + * Message to display above text fields. + */ + message?: string; + /** + * Custom label for the text displayed in front of the filename text field. + */ + nameFieldLabel?: string; + /** + * Show the tags input box, defaults to true. + */ + showsTagField?: boolean; + /** + * Create a when packaged for the Mac App Store. If this option is enabled and the + * file doesn't already exist a blank file will be created at the chosen path. + */ + securityScopedBookmarks?: boolean; + } + interface Settings { /** * true to open the app at login, false to remove the app as a login item. Defaults @@ -9112,14 +9940,17 @@ declare namespace Electron { types: string[]; /** * The size that the media source thumbnail should be scaled to. Default is 150 x - * 150. + * 150. Set width or height to 0 when you do not need the thumbnails. This will + * save the processing time required for capturing the content of each window and + * screen. */ thumbnailSize?: Size; - } - - interface StartMonitoringOptions { - categoryFilter: string; - traceOptions: string; + /** + * Set to true to enable fetching window icons. The default value is false. When + * false the appIcon property of the sources return null. Same if a source has the + * type screen. + */ + fetchWindowIcons?: boolean; } interface SystemMemoryInfo { @@ -9487,7 +10318,7 @@ declare namespace Electron { */ devTools?: boolean; /** - * Whether node integration is enabled. Default is true. + * Whether node integration is enabled. Default is false. */ nodeIntegration?: boolean; /** @@ -9495,6 +10326,12 @@ declare namespace Electron { * this can be found in . */ nodeIntegrationInWorker?: boolean; + /** + * Experimental option for enabling Node.js support in sub-frames such as iframes + * and child windows. All your preloads will load for every iframe, you can use + * process.isMainFrame to determine if you are in the main frame or not. + */ + nodeIntegrationInSubFrames?: boolean; /** * Specifies a script that will be loaded before other scripts run in the page. * This script will always have access to node APIs no matter whether node @@ -9571,10 +10408,6 @@ declare namespace Electron { * Enables WebGL support. Default is true. */ webgl?: boolean; - /** - * Enables WebAudio support. Default is true. - */ - webaudio?: boolean; /** * Whether plugins should be enabled. Default is false. */ @@ -9642,19 +10475,17 @@ declare namespace Electron { */ contextIsolation?: boolean; /** - * Whether to use native window.open(). If set to true, the webPreferences of child - * window will always be the same with parent window, regardless of the parameters - * passed to window.open(). Defaults to false. This option is currently - * experimental. + * Whether to use native window.open(). Defaults to false. Child windows will + * always have node integration disabled unless nodeIntegrationInSubFrames is true. + * This option is currently experimental. */ nativeWindowOpen?: boolean; /** - * Whether to enable the . Defaults to the value of the nodeIntegration option. The - * preload script configured for the will have node integration enabled when it is - * executed so you should ensure remote/untrusted content is not able to create a - * tag with a possibly malicious preload script. You can use the - * will-attach-webview event on to strip away the preload script and to validate or - * alter the 's initial settings. + * Whether to enable the . Defaults to false. The preload script configured for the + * will have node integration enabled when it is executed so you should ensure + * remote/untrusted content is not able to create a tag with a possibly malicious + * preload script. You can use the will-attach-webview event on to strip away the + * preload script and to validate or alter the 's initial settings. */ webviewTag?: boolean; /** @@ -9678,6 +10509,17 @@ declare namespace Electron { * Default is false. */ navigateOnDragDrop?: boolean; + /** + * Autoplay policy to apply to content in the window, can be + * no-user-gesture-required, user-gesture-required, + * document-user-activation-required. Defaults to no-user-gesture-required. + */ + autoplayPolicy?: ('no-user-gesture-required' | 'user-gesture-required' | 'document-user-activation-required'); + /** + * Whether to prevent the window from resizing when entering HTML Fullscreen. + * Default is false. + */ + disableHtmlFullscreenWindowResize?: boolean; } interface DefaultFontFamily { @@ -9750,7 +10592,6 @@ declare namespace NodeJS { // addListener(event: 'loaded', listener: Function): this; // removeListener(event: 'loaded', listener: Function): this; // ### END VSCODE MODIFICATION ### - /** * Causes the main thread of the current process crash. */ @@ -9777,12 +10618,17 @@ declare namespace NodeJS { * private memory is more representative of the actual pre-compression memory usage * of the process on macOS. */ - getProcessMemoryInfo(): Electron.ProcessMemoryInfo; + getProcessMemoryInfo(): Promise; /** * Returns an object giving memory usage statistics about the entire system. Note * that all statistics are reported in Kilobytes. */ getSystemMemoryInfo(): Electron.SystemMemoryInfo; + /** + * Examples: Note: It returns the actual operating system version instead of kernel + * version on macOS unlike os.release(). + */ + getSystemVersion(): string; /** * Causes the main thread of the current process hang. */ @@ -9801,6 +10647,17 @@ declare namespace NodeJS { * this property is true in the main process, otherwise it is undefined. */ defaultApp?: boolean; + /** + * A Boolean that controls whether or not deprecation warnings are printed to + * stderr when formerly callback-based APIs converted to Promises are invoked using + * callbacks. Setting this to true will enable deprecation warnings. + */ + enablePromiseAPIs?: boolean; + /** + * A Boolean, true when the current renderer context is the "main" renderer frame. + * If you want the ID of the current frame you should use webFrame.routingId. + */ + isMainFrame?: boolean; /** * A Boolean. For Mac App Store build, this property is true, for other builds it * is undefined. @@ -9848,7 +10705,7 @@ declare namespace NodeJS { traceProcessWarnings?: boolean; /** * A String representing the current process's type, can be "browser" (i.e. main - * process) or "renderer". + * process), "renderer", or "worker" (i.e. web worker). */ type?: string; /** diff --git a/src/vs/base/browser/fastDomNode.ts b/src/vs/base/browser/fastDomNode.ts index cb121546529..6dfec19f492 100644 --- a/src/vs/base/browser/fastDomNode.ts +++ b/src/vs/base/browser/fastDomNode.ts @@ -18,6 +18,7 @@ export class FastDomNode { private _fontFamily: string; private _fontWeight: string; private _fontSize: number; + private _fontFeatureSettings: string; private _lineHeight: number; private _letterSpacing: number; private _className: string; @@ -38,6 +39,7 @@ export class FastDomNode { this._fontFamily = ''; this._fontWeight = ''; this._fontSize = -1; + this._fontFeatureSettings = ''; this._lineHeight = -1; this._letterSpacing = -100; this._className = ''; @@ -135,6 +137,14 @@ export class FastDomNode { this.domNode.style.fontSize = this._fontSize + 'px'; } + public setFontFeatureSettings(fontFeatureSettings: string): void { + if (this._fontFeatureSettings === fontFeatureSettings) { + return; + } + this._fontFeatureSettings = fontFeatureSettings; + this.domNode.style.fontFeatureSettings = this._fontFeatureSettings; + } + public setLineHeight(lineHeight: number): void { if (this._lineHeight === lineHeight) { return; diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index ce270d0dda1..f3f2089100b 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -152,8 +152,16 @@ export class StandardWheelEvent { this.deltaX = deltaX; if (e) { - if (e.type === 'wheel') { + // Old (deprecated) wheel events + let e1 = e; + let e2 = e; + // vertical delta scroll + if (typeof e1.wheelDeltaY !== 'undefined') { + this.deltaY = e1.wheelDeltaY / 120; + } else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) { + this.deltaY = -e2.detail / 3; + } else if (e.type === 'wheel') { // Modern wheel event // https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent const ev = e; @@ -161,39 +169,36 @@ export class StandardWheelEvent { if (ev.deltaMode === ev.DOM_DELTA_LINE) { // the deltas are expressed in lines this.deltaY = -e.deltaY; - this.deltaX = -e.deltaX; } else { this.deltaY = -e.deltaY / 40; + } + } + + // horizontal delta scroll + if (typeof e1.wheelDeltaX !== 'undefined') { + if (browser.isSafari && platform.isWindows) { + this.deltaX = - (e1.wheelDeltaX / 120); + } else { + this.deltaX = e1.wheelDeltaX / 120; + } + } else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) { + this.deltaX = -e.detail / 3; + } else if (e.type === 'wheel') { + // Modern wheel event + // https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent + const ev = e; + + if (ev.deltaMode === ev.DOM_DELTA_LINE) { + // the deltas are expressed in lines + this.deltaX = -e.deltaX; + } else { this.deltaX = -e.deltaX / 40; } + } - } else { - // Old (deprecated) wheel events - let e1 = e; - let e2 = e; - - // vertical delta scroll - if (typeof e1.wheelDeltaY !== 'undefined') { - this.deltaY = e1.wheelDeltaY / 120; - } else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) { - this.deltaY = -e2.detail / 3; - } - - // horizontal delta scroll - if (typeof e1.wheelDeltaX !== 'undefined') { - if (browser.isSafari && platform.isWindows) { - this.deltaX = - (e1.wheelDeltaX / 120); - } else { - this.deltaX = e1.wheelDeltaX / 120; - } - } else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) { - this.deltaX = -e.detail / 3; - } - - // Assume a vertical scroll if nothing else worked - if (this.deltaY === 0 && this.deltaX === 0 && e.wheelDelta) { - this.deltaY = e.wheelDelta / 120; - } + // Assume a vertical scroll if nothing else worked + if (this.deltaY === 0 && this.deltaX === 0 && e.wheelDelta) { + this.deltaY = e.wheelDelta / 120; } } } diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index 4519d7e0e16..bef90a94d4d 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -67,7 +67,7 @@ export class Gesture extends Disposable { private static readonly SCROLL_FRICTION = -0.005; private static INSTANCE: Gesture; - private static HOLD_DELAY = 700; + private static readonly HOLD_DELAY = 700; private dispatched = false; private targets: HTMLElement[]; diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 43c76fb28d4..27925b09b9c 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -33,11 +33,12 @@ export interface IBaseActionViewItemOptions { export class BaseActionViewItem extends Disposable implements IActionViewItem { - element?: HTMLElement; + element: HTMLElement | undefined; + _context: any; _action: IAction; - private _actionRunner!: IActionRunner; + private _actionRunner: IActionRunner | undefined; constructor(context: any, action: IAction, protected options?: IBaseActionViewItemOptions) { super(); @@ -81,12 +82,16 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { } } - set actionRunner(actionRunner: IActionRunner) { - this._actionRunner = actionRunner; + get actionRunner(): IActionRunner { + if (!this._actionRunner) { + this._actionRunner = this._register(new ActionRunner()); + } + + return this._actionRunner; } - get actionRunner(): IActionRunner { - return this._actionRunner; + set actionRunner(actionRunner: IActionRunner) { + this._actionRunner = actionRunner; } getAction(): IAction { @@ -102,7 +107,7 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { } render(container: HTMLElement): void { - this.element = container; + const element = this.element = container; this._register(Gesture.addTarget(container)); const enableDragging = this.options && this.options.draggable; @@ -110,19 +115,19 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { container.draggable = true; } - this._register(DOM.addDisposableListener(this.element, EventType.Tap, e => this.onClick(e))); + this._register(DOM.addDisposableListener(element, EventType.Tap, e => this.onClick(e))); - this._register(DOM.addDisposableListener(this.element, DOM.EventType.MOUSE_DOWN, e => { + this._register(DOM.addDisposableListener(element, DOM.EventType.MOUSE_DOWN, e => { if (!enableDragging) { DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it } - if (this._action.enabled && e.button === 0 && this.element) { - DOM.addClass(this.element, 'active'); + if (this._action.enabled && e.button === 0) { + DOM.addClass(element, 'active'); } })); - this._register(DOM.addDisposableListener(this.element, DOM.EventType.CLICK, e => { + this._register(DOM.addDisposableListener(element, DOM.EventType.CLICK, e => { DOM.EventHelper.stop(e, true); // See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard // > Writing to the clipboard @@ -139,14 +144,14 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { } })); - this._register(DOM.addDisposableListener(this.element, DOM.EventType.DBLCLICK, e => { + this._register(DOM.addDisposableListener(element, DOM.EventType.DBLCLICK, e => { DOM.EventHelper.stop(e, true); })); [DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT].forEach(event => { - this._register(DOM.addDisposableListener(this.element!, event, e => { + this._register(DOM.addDisposableListener(element, event, e => { DOM.EventHelper.stop(e); - DOM.removeClass(this.element!, 'active'); + DOM.removeClass(element, 'active'); })); }); } @@ -165,7 +170,7 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { } } - this._actionRunner.run(this._action, context); + this.actionRunner.run(this._action, context); } focus(): void { @@ -232,7 +237,7 @@ export interface IActionViewItemOptions extends IBaseActionViewItemOptions { export class ActionViewItem extends BaseActionViewItem { - protected label!: HTMLElement; + protected label: HTMLElement | undefined; protected options: IActionViewItemOptions; private cssClass?: string; @@ -252,13 +257,17 @@ export class ActionViewItem extends BaseActionViewItem { if (this.element) { this.label = DOM.append(this.element, DOM.$('a.action-label')); } - if (this._action.id === Separator.ID) { - this.label.setAttribute('role', 'presentation'); // A separator is a presentation item - } else { - if (this.options.isMenu) { - this.label.setAttribute('role', 'menuitem'); + + + if (this.label) { + if (this._action.id === Separator.ID) { + this.label.setAttribute('role', 'presentation'); // A separator is a presentation item } else { - this.label.setAttribute('role', 'button'); + if (this.options.isMenu) { + this.label.setAttribute('role', 'menuitem'); + } else { + this.label.setAttribute('role', 'button'); + } } } @@ -276,11 +285,13 @@ export class ActionViewItem extends BaseActionViewItem { focus(): void { super.focus(); - this.label.focus(); + if (this.label) { + this.label.focus(); + } } updateLabel(): void { - if (this.options.label) { + if (this.options.label && this.label) { this.label.textContent = this.getAction().label; } } @@ -299,52 +310,65 @@ export class ActionViewItem extends BaseActionViewItem { } } - if (title) { + if (title && this.label) { this.label.title = title; } } updateClass(): void { - if (this.cssClass) { + if (this.cssClass && this.label) { DOM.removeClasses(this.label, this.cssClass); } if (this.options.icon) { this.cssClass = this.getAction().class; - DOM.addClass(this.label, 'codicon'); - if (this.cssClass) { - DOM.addClasses(this.label, this.cssClass); + + if (this.label) { + DOM.addClass(this.label, 'codicon'); + if (this.cssClass) { + DOM.addClasses(this.label, this.cssClass); + } } this.updateEnabled(); } else { - DOM.removeClass(this.label, 'codicon'); + if (this.label) { + DOM.removeClass(this.label, 'codicon'); + } } } updateEnabled(): void { if (this.getAction().enabled) { - this.label.removeAttribute('aria-disabled'); + if (this.label) { + this.label.removeAttribute('aria-disabled'); + DOM.removeClass(this.label, 'disabled'); + this.label.tabIndex = 0; + } + if (this.element) { DOM.removeClass(this.element, 'disabled'); } - DOM.removeClass(this.label, 'disabled'); - this.label.tabIndex = 0; } else { - this.label.setAttribute('aria-disabled', 'true'); + if (this.label) { + this.label.setAttribute('aria-disabled', 'true'); + DOM.addClass(this.label, 'disabled'); + DOM.removeTabIndexAndUpdateFocus(this.label); + } + if (this.element) { DOM.addClass(this.element, 'disabled'); } - DOM.addClass(this.label, 'disabled'); - DOM.removeTabIndexAndUpdateFocus(this.label); } } updateChecked(): void { - if (this.getAction().checked) { - DOM.addClass(this.label, 'checked'); - } else { - DOM.removeClass(this.label, 'checked'); + if (this.label) { + if (this.getAction().checked) { + DOM.addClass(this.label, 'checked'); + } else { + DOM.removeClass(this.label, 'checked'); + } } } } diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index a982d959b1c..de4d35ef320 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -79,7 +79,7 @@ export class Button extends Disposable { this._register(DOM.addDisposableListener(this._element, DOM.EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); let eventHandled = false; - if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + if (this.enabled && (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { this._onDidClick.fire(e); eventHandled = true; } else if (event.equals(KeyCode.Escape)) { @@ -128,15 +128,15 @@ export class Button extends Disposable { private applyStyles(): void { if (this._element) { - const background = this.buttonBackground ? this.buttonBackground.toString() : null; + const background = this.buttonBackground ? this.buttonBackground.toString() : ''; const foreground = this.buttonForeground ? this.buttonForeground.toString() : null; - const border = this.buttonBorder ? this.buttonBorder.toString() : null; + const border = this.buttonBorder ? this.buttonBorder.toString() : ''; this._element.style.color = foreground; this._element.style.backgroundColor = background; - this._element.style.borderWidth = border ? '1px' : null; - this._element.style.borderStyle = border ? 'solid' : null; + this._element.style.borderWidth = border ? '1px' : ''; + this._element.style.borderStyle = border ? 'solid' : ''; this._element.style.borderColor = border; } } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 6bc9a8e3d4c..afc9cc616a0 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -38,7 +38,7 @@ const defaultOpts = { export class CheckboxActionViewItem extends BaseActionViewItem { - private checkbox!: Checkbox; + private checkbox: Checkbox | undefined; private readonly disposables = new DisposableStore(); render(container: HTMLElement): void { @@ -51,7 +51,7 @@ export class CheckboxActionViewItem extends BaseActionViewItem { title: this._action.label }); this.disposables.add(this.checkbox); - this.disposables.add(this.checkbox.onChange(() => this._action.checked = this.checkbox!.checked, this)); + this.disposables.add(this.checkbox.onChange(() => this._action.checked = !!this.checkbox && this.checkbox.checked, this)); this.element.appendChild(this.checkbox.domNode); } @@ -219,7 +219,7 @@ export class SimpleCheckbox extends Widget { protected applyStyles(): void { this.domNode.style.color = this.styles.checkboxForeground ? this.styles.checkboxForeground.toString() : null; - this.domNode.style.backgroundColor = this.styles.checkboxBackground ? this.styles.checkboxBackground.toString() : null; - this.domNode.style.borderColor = this.styles.checkboxBorder ? this.styles.checkboxBorder.toString() : null; + this.domNode.style.backgroundColor = this.styles.checkboxBackground ? this.styles.checkboxBackground.toString() : ''; + this.domNode.style.borderColor = this.styles.checkboxBorder ? this.styles.checkboxBorder.toString() : ''; } } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index cd87edb95cf..2ef19ad074b 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?3b584136fb1f0186a1ee578cdcb5240b") format("truetype"); + src: url("./codicon.ttf?419224353c2d776235f324ad51a4eac2") format("truetype"); } .codicon[class*='codicon-'] { @@ -139,217 +139,221 @@ .codicon-chevron-left:before { content: "\f122" } .codicon-chevron-right:before { content: "\f123" } .codicon-chevron-up:before { content: "\f124" } -.codicon-circle-outline:before { content: "\f125" } -.codicon-circle-slash:before { content: "\f126" } -.codicon-circuit-board:before { content: "\f127" } -.codicon-class:before { content: "\f128" } -.codicon-clear-all:before { content: "\f129" } -.codicon-clippy:before { content: "\f12a" } -.codicon-close-all:before { content: "\f12b" } -.codicon-cloud-download:before { content: "\f12c" } -.codicon-cloud-upload:before { content: "\f12d" } -.codicon-code:before { content: "\f12e" } -.codicon-collapse-all:before { content: "\f12f" } -.codicon-color-mode:before { content: "\f130" } -.codicon-color:before { content: "\f131" } -.codicon-comment-discussion:before { content: "\f132" } -.codicon-compare-changes:before { content: "\f133" } -.codicon-console:before { content: "\f134" } -.codicon-constant:before { content: "\f135" } -.codicon-continue:before { content: "\f136" } -.codicon-credit-card:before { content: "\f137" } -.codicon-current-and-breakpoint:before { content: "\f138" } -.codicon-current:before { content: "\f139" } -.codicon-dash:before { content: "\f13a" } -.codicon-dashboard:before { content: "\f13b" } -.codicon-database:before { content: "\f13c" } -.codicon-debug:before { content: "\f13d" } -.codicon-device-camera-video:before { content: "\f13e" } -.codicon-device-camera:before { content: "\f13f" } -.codicon-device-mobile:before { content: "\f140" } -.codicon-diff-added:before { content: "\f141" } -.codicon-diff-ignored:before { content: "\f142" } -.codicon-diff-modified:before { content: "\f143" } -.codicon-diff-removed:before { content: "\f144" } -.codicon-diff-renamed:before { content: "\f145" } -.codicon-diff:before { content: "\f146" } -.codicon-discard:before { content: "\f147" } -.codicon-disconnect-:before { content: "\f148" } -.codicon-editor-layout:before { content: "\f149" } -.codicon-ellipsis:before { content: "\f14a" } -.codicon-empty-window:before { content: "\f14b" } -.codicon-enumerator-member:before { content: "\f14c" } -.codicon-enumerator:before { content: "\f14d" } -.codicon-error:before { content: "\f14e" } -.codicon-event:before { content: "\f14f" } -.codicon-exclude:before { content: "\f150" } -.codicon-extensions:before { content: "\f151" } -.codicon-eye-closed:before { content: "\f152" } -.codicon-field:before { content: "\f153" } -.codicon-file-binary:before { content: "\f154" } -.codicon-file-code:before { content: "\f155" } -.codicon-file-media:before { content: "\f156" } -.codicon-file-pdf:before { content: "\f157" } -.codicon-file-submodule:before { content: "\f158" } -.codicon-file-symlink-directory:before { content: "\f159" } -.codicon-file-symlink-file:before { content: "\f15a" } -.codicon-file-zip:before { content: "\f15b" } -.codicon-files:before { content: "\f15c" } -.codicon-filter:before { content: "\f15d" } -.codicon-flame:before { content: "\f15e" } -.codicon-fold-down:before { content: "\f15f" } -.codicon-fold-up:before { content: "\f160" } -.codicon-fold:before { content: "\f161" } -.codicon-folder-active:before { content: "\f162" } -.codicon-folder-opened:before { content: "\f163" } -.codicon-folder:before { content: "\f164" } -.codicon-gear:before { content: "\f165" } -.codicon-gift:before { content: "\f166" } -.codicon-gist-secret:before { content: "\f167" } -.codicon-gist:before { content: "\f168" } -.codicon-git-commit:before { content: "\f169" } -.codicon-git-compare:before { content: "\f16a" } -.codicon-git-merge:before { content: "\f16b" } -.codicon-github-action:before { content: "\f16c" } -.codicon-github-alt:before { content: "\f16d" } -.codicon-github:before { content: "\f16e" } -.codicon-globe:before { content: "\f16f" } -.codicon-go-to-file:before { content: "\f170" } -.codicon-grabber:before { content: "\f171" } -.codicon-graph:before { content: "\f172" } -.codicon-gripper:before { content: "\f173" } -.codicon-heart:before { content: "\f174" } -.codicon-history:before { content: "\f175" } -.codicon-home:before { content: "\f176" } -.codicon-horizontal-rule:before { content: "\f177" } -.codicon-hubot:before { content: "\f178" } -.codicon-inbox:before { content: "\f179" } -.codicon-interface:before { content: "\f17a" } -.codicon-issue-closed:before { content: "\f17b" } -.codicon-issue-reopened:before { content: "\f17c" } -.codicon-issues:before { content: "\f17d" } -.codicon-italic:before { content: "\f17e" } -.codicon-jersey:before { content: "\f17f" } -.codicon-json:before { content: "\f180" } -.codicon-kebab-vertical:before { content: "\f181" } -.codicon-key:before { content: "\f182" } -.codicon-keyword:before { content: "\f183" } -.codicon-law:before { content: "\f184" } -.codicon-lightbulb-autofix:before { content: "\f185" } -.codicon-link-external:before { content: "\f186" } -.codicon-link:before { content: "\f187" } -.codicon-list-ordered:before { content: "\f188" } -.codicon-list-unordered:before { content: "\f189" } -.codicon-live-share:before { content: "\f18a" } -.codicon-loading:before { content: "\f18b" } -.codicon-location:before { content: "\f18c" } -.codicon-mail-read:before { content: "\f18d" } -.codicon-mail:before { content: "\f18e" } -.codicon-markdown:before { content: "\f18f" } -.codicon-megaphone:before { content: "\f190" } -.codicon-mention:before { content: "\f191" } -.codicon-method:before { content: "\f192" } -.codicon-milestone:before { content: "\f193" } -.codicon-misc:before { content: "\f194" } -.codicon-mortar-board:before { content: "\f195" } -.codicon-move:before { content: "\f196" } -.codicon-multiple-windows:before { content: "\f197" } -.codicon-mute:before { content: "\f198" } -.codicon-namespace:before { content: "\f199" } -.codicon-no-newline:before { content: "\f19a" } -.codicon-note:before { content: "\f19b" } -.codicon-numeric:before { content: "\f19c" } -.codicon-octoface:before { content: "\f19d" } -.codicon-open-preview:before { content: "\f19e" } -.codicon-operator:before { content: "\f19f" } -.codicon-package:before { content: "\f1a0" } -.codicon-paintcan:before { content: "\f1a1" } -.codicon-parameter:before { content: "\f1a2" } -.codicon-pause:before { content: "\f1a3" } -.codicon-pin:before { content: "\f1a4" } -.codicon-play:before { content: "\f1a5" } -.codicon-plug:before { content: "\f1a6" } -.codicon-preserve-case:before { content: "\f1a7" } -.codicon-preview:before { content: "\f1a8" } -.codicon-project:before { content: "\f1a9" } -.codicon-property:before { content: "\f1aa" } -.codicon-pulse:before { content: "\f1ab" } -.codicon-question:before { content: "\f1ac" } -.codicon-quote:before { content: "\f1ad" } -.codicon-radio-tower:before { content: "\f1ae" } -.codicon-reactions:before { content: "\f1af" } -.codicon-references:before { content: "\f1b0" } -.codicon-refresh:before { content: "\f1b1" } -.codicon-regex:before { content: "\f1b2" } -.codicon-remote:before { content: "\f1b3" } -.codicon-remove:before { content: "\f1b4" } -.codicon-replace-all:before { content: "\f1b5" } -.codicon-replace:before { content: "\f1b6" } -.codicon-repo-clone:before { content: "\f1b7" } -.codicon-repo-force-push:before { content: "\f1b8" } -.codicon-repo-pull:before { content: "\f1b9" } -.codicon-repo-push:before { content: "\f1ba" } -.codicon-report:before { content: "\f1bb" } -.codicon-request-changes:before { content: "\f1bc" } -.codicon-restart:before { content: "\f1bd" } -.codicon-rocket:before { content: "\f1be" } -.codicon-root-folder-opened:before { content: "\f1bf" } -.codicon-root-folder:before { content: "\f1c0" } -.codicon-rss:before { content: "\f1c1" } -.codicon-ruby:before { content: "\f1c2" } -.codicon-ruler:before { content: "\f1c3" } -.codicon-save-all:before { content: "\f1c4" } -.codicon-save-as:before { content: "\f1c5" } -.codicon-save:before { content: "\f1c6" } -.codicon-screen-full:before { content: "\f1c7" } -.codicon-screen-normal:before { content: "\f1c8" } -.codicon-search-stop:before { content: "\f1c9" } -.codicon-selection:before { content: "\f1ca" } -.codicon-server:before { content: "\f1cb" } -.codicon-settings:before { content: "\f1cc" } -.codicon-shield:before { content: "\f1cd" } -.codicon-smiley:before { content: "\f1ce" } -.codicon-snippet:before { content: "\f1cf" } -.codicon-sort-precedence:before { content: "\f1d0" } -.codicon-split-horizontal:before { content: "\f1d1" } -.codicon-split-vertical:before { content: "\f1d2" } -.codicon-squirrel:before { content: "\f1d3" } -.codicon-star-empty:before { content: "\f1d4" } -.codicon-star-full:before { content: "\f1d5" } -.codicon-star-half:before { content: "\f1d6" } -.codicon-start:before { content: "\f1d7" } -.codicon-step-into:before { content: "\f1d8" } -.codicon-step-out:before { content: "\f1d9" } -.codicon-step-over:before { content: "\f1da" } -.codicon-string:before { content: "\f1db" } -.codicon-structure:before { content: "\f1dc" } -.codicon-tasklist:before { content: "\f1dd" } -.codicon-telescope:before { content: "\f1de" } -.codicon-text-size:before { content: "\f1df" } -.codicon-three-bars:before { content: "\f1e0" } -.codicon-thumbsdown:before { content: "\f1e1" } -.codicon-thumbsup:before { content: "\f1e2" } -.codicon-tools:before { content: "\f1e3" } -.codicon-trash:before { content: "\f1e4" } -.codicon-triangle-down:before { content: "\f1e5" } -.codicon-triangle-left:before { content: "\f1e6" } -.codicon-triangle-right:before { content: "\f1e7" } -.codicon-triangle-up:before { content: "\f1e8" } -.codicon-twitter:before { content: "\f1e9" } -.codicon-unfold:before { content: "\f1ea" } -.codicon-unlock:before { content: "\f1eb" } -.codicon-unmute:before { content: "\f1ec" } -.codicon-unverified:before { content: "\f1ed" } -.codicon-variable:before { content: "\f1ee" } -.codicon-verified:before { content: "\f1ef" } -.codicon-versions:before { content: "\f1f0" } -.codicon-vm-active:before { content: "\f1f1" } -.codicon-vm-outline:before { content: "\f1f2" } -.codicon-vm-running:before { content: "\f1f3" } -.codicon-watch:before { content: "\f1f4" } -.codicon-whitespace:before { content: "\f1f5" } -.codicon-whole-word:before { content: "\f1f6" } -.codicon-window:before { content: "\f1f7" } -.codicon-word-wrap:before { content: "\f1f8" } -.codicon-zoom-in:before { content: "\f1f9" } -.codicon-zoom-out:before { content: "\f1fa" } +.codicon-chrome-close:before { content: "\f125" } +.codicon-chrome-maximize:before { content: "\f126" } +.codicon-chrome-minimize:before { content: "\f127" } +.codicon-chrome-restore:before { content: "\f128" } +.codicon-circle-outline:before { content: "\f129" } +.codicon-circle-slash:before { content: "\f12a" } +.codicon-circuit-board:before { content: "\f12b" } +.codicon-class:before { content: "\f12c" } +.codicon-clear-all:before { content: "\f12d" } +.codicon-clippy:before { content: "\f12e" } +.codicon-close-all:before { content: "\f12f" } +.codicon-cloud-download:before { content: "\f130" } +.codicon-cloud-upload:before { content: "\f131" } +.codicon-code:before { content: "\f132" } +.codicon-collapse-all:before { content: "\f133" } +.codicon-color-mode:before { content: "\f134" } +.codicon-color:before { content: "\f135" } +.codicon-comment-discussion:before { content: "\f136" } +.codicon-compare-changes:before { content: "\f137" } +.codicon-console:before { content: "\f138" } +.codicon-constant:before { content: "\f139" } +.codicon-continue:before { content: "\f13a" } +.codicon-credit-card:before { content: "\f13b" } +.codicon-current-and-breakpoint:before { content: "\f13c" } +.codicon-current:before { content: "\f13d" } +.codicon-dash:before { content: "\f13e" } +.codicon-dashboard:before { content: "\f13f" } +.codicon-database:before { content: "\f140" } +.codicon-debug:before { content: "\f141" } +.codicon-device-camera-video:before { content: "\f142" } +.codicon-device-camera:before { content: "\f143" } +.codicon-device-mobile:before { content: "\f144" } +.codicon-diff-added:before { content: "\f145" } +.codicon-diff-ignored:before { content: "\f146" } +.codicon-diff-modified:before { content: "\f147" } +.codicon-diff-removed:before { content: "\f148" } +.codicon-diff-renamed:before { content: "\f149" } +.codicon-diff:before { content: "\f14a" } +.codicon-discard:before { content: "\f14b" } +.codicon-disconnect-:before { content: "\f14c" } +.codicon-editor-layout:before { content: "\f14d" } +.codicon-ellipsis:before { content: "\f14e" } +.codicon-empty-window:before { content: "\f14f" } +.codicon-enumerator-member:before { content: "\f150" } +.codicon-enumerator:before { content: "\f151" } +.codicon-error:before { content: "\f152" } +.codicon-event:before { content: "\f153" } +.codicon-exclude:before { content: "\f154" } +.codicon-extensions:before { content: "\f155" } +.codicon-eye-closed:before { content: "\f156" } +.codicon-field:before { content: "\f157" } +.codicon-file-binary:before { content: "\f158" } +.codicon-file-code:before { content: "\f159" } +.codicon-file-media:before { content: "\f15a" } +.codicon-file-pdf:before { content: "\f15b" } +.codicon-file-submodule:before { content: "\f15c" } +.codicon-file-symlink-directory:before { content: "\f15d" } +.codicon-file-symlink-file:before { content: "\f15e" } +.codicon-file-zip:before { content: "\f15f" } +.codicon-files:before { content: "\f160" } +.codicon-filter:before { content: "\f161" } +.codicon-flame:before { content: "\f162" } +.codicon-fold-down:before { content: "\f163" } +.codicon-fold-up:before { content: "\f164" } +.codicon-fold:before { content: "\f165" } +.codicon-folder-active:before { content: "\f166" } +.codicon-folder-opened:before { content: "\f167" } +.codicon-folder:before { content: "\f168" } +.codicon-gear:before { content: "\f169" } +.codicon-gift:before { content: "\f16a" } +.codicon-gist-secret:before { content: "\f16b" } +.codicon-gist:before { content: "\f16c" } +.codicon-git-commit:before { content: "\f16d" } +.codicon-git-compare:before { content: "\f16e" } +.codicon-git-merge:before { content: "\f16f" } +.codicon-github-action:before { content: "\f170" } +.codicon-github-alt:before { content: "\f171" } +.codicon-github:before { content: "\f172" } +.codicon-globe:before { content: "\f173" } +.codicon-go-to-file:before { content: "\f174" } +.codicon-grabber:before { content: "\f175" } +.codicon-graph:before { content: "\f176" } +.codicon-gripper:before { content: "\f177" } +.codicon-heart:before { content: "\f178" } +.codicon-history:before { content: "\f179" } +.codicon-home:before { content: "\f17a" } +.codicon-horizontal-rule:before { content: "\f17b" } +.codicon-hubot:before { content: "\f17c" } +.codicon-inbox:before { content: "\f17d" } +.codicon-interface:before { content: "\f17e" } +.codicon-issue-closed:before { content: "\f17f" } +.codicon-issue-reopened:before { content: "\f180" } +.codicon-issues:before { content: "\f181" } +.codicon-italic:before { content: "\f182" } +.codicon-jersey:before { content: "\f183" } +.codicon-json:before { content: "\f184" } +.codicon-kebab-vertical:before { content: "\f185" } +.codicon-key:before { content: "\f186" } +.codicon-keyword:before { content: "\f187" } +.codicon-law:before { content: "\f188" } +.codicon-lightbulb-autofix:before { content: "\f189" } +.codicon-link-external:before { content: "\f18a" } +.codicon-link:before { content: "\f18b" } +.codicon-list-ordered:before { content: "\f18c" } +.codicon-list-unordered:before { content: "\f18d" } +.codicon-live-share:before { content: "\f18e" } +.codicon-loading:before { content: "\f18f" } +.codicon-location:before { content: "\f190" } +.codicon-mail-read:before { content: "\f191" } +.codicon-mail:before { content: "\f192" } +.codicon-markdown:before { content: "\f193" } +.codicon-megaphone:before { content: "\f194" } +.codicon-mention:before { content: "\f195" } +.codicon-method:before { content: "\f196" } +.codicon-milestone:before { content: "\f197" } +.codicon-misc:before { content: "\f198" } +.codicon-mortar-board:before { content: "\f199" } +.codicon-move:before { content: "\f19a" } +.codicon-multiple-windows:before { content: "\f19b" } +.codicon-mute:before { content: "\f19c" } +.codicon-namespace:before { content: "\f19d" } +.codicon-no-newline:before { content: "\f19e" } +.codicon-note:before { content: "\f19f" } +.codicon-numeric:before { content: "\f1a0" } +.codicon-octoface:before { content: "\f1a1" } +.codicon-open-preview:before { content: "\f1a2" } +.codicon-operator:before { content: "\f1a3" } +.codicon-package:before { content: "\f1a4" } +.codicon-paintcan:before { content: "\f1a5" } +.codicon-parameter:before { content: "\f1a6" } +.codicon-pause:before { content: "\f1a7" } +.codicon-pin:before { content: "\f1a8" } +.codicon-play:before { content: "\f1a9" } +.codicon-plug:before { content: "\f1aa" } +.codicon-preserve-case:before { content: "\f1ab" } +.codicon-preview:before { content: "\f1ac" } +.codicon-project:before { content: "\f1ad" } +.codicon-property:before { content: "\f1ae" } +.codicon-pulse:before { content: "\f1af" } +.codicon-question:before { content: "\f1b0" } +.codicon-quote:before { content: "\f1b1" } +.codicon-radio-tower:before { content: "\f1b2" } +.codicon-reactions:before { content: "\f1b3" } +.codicon-references:before { content: "\f1b4" } +.codicon-refresh:before { content: "\f1b5" } +.codicon-regex:before { content: "\f1b6" } +.codicon-remote:before { content: "\f1b7" } +.codicon-remove:before { content: "\f1b8" } +.codicon-replace-all:before { content: "\f1b9" } +.codicon-replace:before { content: "\f1ba" } +.codicon-repo-clone:before { content: "\f1bb" } +.codicon-repo-force-push:before { content: "\f1bc" } +.codicon-repo-pull:before { content: "\f1bd" } +.codicon-repo-push:before { content: "\f1be" } +.codicon-report:before { content: "\f1bf" } +.codicon-request-changes:before { content: "\f1c0" } +.codicon-restart:before { content: "\f1c1" } +.codicon-rocket:before { content: "\f1c2" } +.codicon-root-folder-opened:before { content: "\f1c3" } +.codicon-root-folder:before { content: "\f1c4" } +.codicon-rss:before { content: "\f1c5" } +.codicon-ruby:before { content: "\f1c6" } +.codicon-ruler:before { content: "\f1c7" } +.codicon-save-all:before { content: "\f1c8" } +.codicon-save-as:before { content: "\f1c9" } +.codicon-save:before { content: "\f1ca" } +.codicon-screen-full:before { content: "\f1cb" } +.codicon-screen-normal:before { content: "\f1cc" } +.codicon-search-stop:before { content: "\f1cd" } +.codicon-selection:before { content: "\f1ce" } +.codicon-server:before { content: "\f1cf" } +.codicon-settings:before { content: "\f1d0" } +.codicon-shield:before { content: "\f1d1" } +.codicon-smiley:before { content: "\f1d2" } +.codicon-snippet:before { content: "\f1d3" } +.codicon-sort-precedence:before { content: "\f1d4" } +.codicon-split-horizontal:before { content: "\f1d5" } +.codicon-split-vertical:before { content: "\f1d6" } +.codicon-squirrel:before { content: "\f1d7" } +.codicon-star-empty:before { content: "\f1d8" } +.codicon-star-full:before { content: "\f1d9" } +.codicon-star-half:before { content: "\f1da" } +.codicon-start:before { content: "\f1db" } +.codicon-step-into:before { content: "\f1dc" } +.codicon-step-out:before { content: "\f1dd" } +.codicon-step-over:before { content: "\f1de" } +.codicon-string:before { content: "\f1df" } +.codicon-structure:before { content: "\f1e0" } +.codicon-tasklist:before { content: "\f1e1" } +.codicon-telescope:before { content: "\f1e2" } +.codicon-text-size:before { content: "\f1e3" } +.codicon-three-bars:before { content: "\f1e4" } +.codicon-thumbsdown:before { content: "\f1e5" } +.codicon-thumbsup:before { content: "\f1e6" } +.codicon-tools:before { content: "\f1e7" } +.codicon-trash:before { content: "\f1e8" } +.codicon-triangle-down:before { content: "\f1e9" } +.codicon-triangle-left:before { content: "\f1ea" } +.codicon-triangle-right:before { content: "\f1eb" } +.codicon-triangle-up:before { content: "\f1ec" } +.codicon-twitter:before { content: "\f1ed" } +.codicon-unfold:before { content: "\f1ee" } +.codicon-unlock:before { content: "\f1ef" } +.codicon-unmute:before { content: "\f1f0" } +.codicon-unverified:before { content: "\f1f1" } +.codicon-variable:before { content: "\f1f2" } +.codicon-verified:before { content: "\f1f3" } +.codicon-versions:before { content: "\f1f4" } +.codicon-vm-active:before { content: "\f1f5" } +.codicon-vm-outline:before { content: "\f1f6" } +.codicon-vm-running:before { content: "\f1f7" } +.codicon-watch:before { content: "\f1f8" } +.codicon-whitespace:before { content: "\f1f9" } +.codicon-whole-word:before { content: "\f1fa" } +.codicon-window:before { content: "\f1fb" } +.codicon-word-wrap:before { content: "\f1fc" } +.codicon-zoom-in:before { content: "\f1fd" } +.codicon-zoom-out:before { content: "\f1fe" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index fff7ab51978..3424ea95314 100644 Binary files a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf and b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts index 42d38948e88..e496fd3a164 100644 --- a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts +++ b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts @@ -8,7 +8,7 @@ import 'vs/css!./codicon/codicon-animations'; import { escape } from 'vs/base/common/strings'; function expand(text: string): string { - return text.replace(/\$\(((.+?)(~(.*?))?)\)/g, (_match, _g1, name, _g3, animation) => { + return text.replace(/\$\((([a-z0-9\-]+?)(~([a-z0-9\-]*?))?)\)/gi, (_match, _g1, name, _g3, animation) => { return ``; }); } diff --git a/src/vs/base/browser/ui/countBadge/countBadge.ts b/src/vs/base/browser/ui/countBadge/countBadge.ts index e4b1f68568f..cb04c5099f3 100644 --- a/src/vs/base/browser/ui/countBadge/countBadge.ts +++ b/src/vs/base/browser/ui/countBadge/countBadge.ts @@ -85,15 +85,15 @@ export class CountBadge { private applyStyles(): void { if (this.element) { - const background = this.badgeBackground ? this.badgeBackground.toString() : null; - const foreground = this.badgeForeground ? this.badgeForeground.toString() : null; - const border = this.badgeBorder ? this.badgeBorder.toString() : null; + const background = this.badgeBackground ? this.badgeBackground.toString() : ''; + const foreground = this.badgeForeground ? this.badgeForeground.toString() : ''; + const border = this.badgeBorder ? this.badgeBorder.toString() : ''; this.element.style.backgroundColor = background; this.element.style.color = foreground; - this.element.style.borderWidth = border ? '1px' : null; - this.element.style.borderStyle = border ? 'solid' : null; + this.element.style.borderWidth = border ? '1px' : ''; + this.element.style.borderStyle = border ? 'solid' : ''; this.element.style.borderColor = border; } } diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index b3c43c0794c..723bf64b975 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -142,7 +142,9 @@ export class Dialog extends Disposable { let eventHandled = false; if (evt.equals(KeyMod.Shift | KeyCode.Tab) || evt.equals(KeyCode.LeftArrow)) { if (!this.checkboxHasFocus && focusedButton === 0) { - this.checkbox!.domNode.focus(); + if (this.checkbox) { + this.checkbox.domNode.focus(); + } this.checkboxHasFocus = true; } else { focusedButton = (this.checkboxHasFocus ? 0 : focusedButton) + buttonGroup.buttons.length - 1; @@ -154,7 +156,9 @@ export class Dialog extends Disposable { eventHandled = true; } else if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow)) { if (!this.checkboxHasFocus && focusedButton === buttonGroup.buttons.length - 1) { - this.checkbox!.domNode.focus(); + if (this.checkbox) { + this.checkbox.domNode.focus(); + } this.checkboxHasFocus = true; } else { focusedButton = this.checkboxHasFocus ? 0 : focusedButton + 1; @@ -237,10 +241,10 @@ export class Dialog extends Disposable { if (this.styles) { const style = this.styles; - const fgColor = style.dialogForeground ? `${style.dialogForeground}` : null; - const bgColor = style.dialogBackground ? `${style.dialogBackground}` : null; - const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : null; - const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : null; + const fgColor = style.dialogForeground ? `${style.dialogForeground}` : ''; + const bgColor = style.dialogBackground ? `${style.dialogBackground}` : ''; + const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : ''; + const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : ''; if (this.element) { this.element.style.color = fgColor; diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index dd219559a59..c36015f710b 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -208,8 +208,8 @@ export interface IDropdownMenuOptions extends IBaseDropdownOptions { export class DropdownMenu extends BaseDropdown { private _contextMenuProvider: IContextMenuProvider; - private _menuOptions: IMenuOptions; - private _actions: ReadonlyArray; + private _menuOptions: IMenuOptions | undefined; + private _actions: ReadonlyArray = []; private actionProvider?: IActionProvider; private menuClassName: string; @@ -222,11 +222,11 @@ export class DropdownMenu extends BaseDropdown { this.menuClassName = options.menuClassName || ''; } - set menuOptions(options: IMenuOptions) { + set menuOptions(options: IMenuOptions | undefined) { this._menuOptions = options; } - get menuOptions(): IMenuOptions { + get menuOptions(): IMenuOptions | undefined { return this._menuOptions; } @@ -256,7 +256,7 @@ export class DropdownMenu extends BaseDropdown { getMenuClassName: () => this.menuClassName, onHide: () => this.onHide(), actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined, - anchorAlignment: this.menuOptions.anchorAlignment + anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT }); } @@ -345,7 +345,11 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { super.setActionContext(newContext); if (this.dropdownMenu) { - this.dropdownMenu.menuOptions.context = newContext; + if (this.dropdownMenu.menuOptions) { + this.dropdownMenu.menuOptions.context = newContext; + } else { + this.dropdownMenu.menuOptions = { context: newContext }; + } } } diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index de448b6a484..5950e261de4 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -123,11 +123,96 @@ export class ReplaceInput extends Widget { this.inputValidationErrorBackground = options.inputValidationErrorBackground; this.inputValidationErrorForeground = options.inputValidationErrorForeground; + const history = options.history || []; const flexibleHeight = !!options.flexibleHeight; const flexibleWidth = !!options.flexibleWidth; const flexibleMaxHeight = options.flexibleMaxHeight; - this.buildDomNode(options.history || [], flexibleHeight, flexibleWidth, flexibleMaxHeight); + this.domNode = document.createElement('div'); + dom.addClass(this.domNode, 'monaco-findInput'); + + this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, { + ariaLabel: this.label || '', + placeholder: this.placeholder || '', + validationOptions: { + validation: this.validation + }, + inputBackground: this.inputBackground, + inputForeground: this.inputForeground, + inputBorder: this.inputBorder, + inputValidationInfoBackground: this.inputValidationInfoBackground, + inputValidationInfoForeground: this.inputValidationInfoForeground, + inputValidationInfoBorder: this.inputValidationInfoBorder, + inputValidationWarningBackground: this.inputValidationWarningBackground, + inputValidationWarningForeground: this.inputValidationWarningForeground, + inputValidationWarningBorder: this.inputValidationWarningBorder, + inputValidationErrorBackground: this.inputValidationErrorBackground, + inputValidationErrorForeground: this.inputValidationErrorForeground, + inputValidationErrorBorder: this.inputValidationErrorBorder, + history, + flexibleHeight, + flexibleWidth, + flexibleMaxHeight + })); + + this.preserveCase = this._register(new PreserveCaseCheckbox({ + appendTitle: '', + isChecked: false, + inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionBackground: this.inputActiveOptionBackground, + })); + this._register(this.preserveCase.onChange(viaKeyboard => { + this._onDidOptionChange.fire(viaKeyboard); + if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { + this.inputBox.focus(); + } + this.validate(); + })); + this._register(this.preserveCase.onKeyDown(e => { + this._onPreserveCaseKeyDown.fire(e); + })); + + if (this._showOptionButtons) { + this.cachedOptionsWidth = this.preserveCase.width(); + } else { + this.cachedOptionsWidth = 0; + } + + // Arrow-Key support to navigate between options + let indexes = [this.preserveCase.domNode]; + this.onkeydown(this.domNode, (event: IKeyboardEvent) => { + if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) { + let index = indexes.indexOf(document.activeElement); + if (index >= 0) { + let newIndex: number = -1; + if (event.equals(KeyCode.RightArrow)) { + newIndex = (index + 1) % indexes.length; + } else if (event.equals(KeyCode.LeftArrow)) { + if (index === 0) { + newIndex = indexes.length - 1; + } else { + newIndex = index - 1; + } + } + + if (event.equals(KeyCode.Escape)) { + indexes[index].blur(); + } else if (newIndex >= 0) { + indexes[newIndex].focus(); + } + + dom.EventHelper.stop(event, true); + } + } + }); + + + let controls = document.createElement('div'); + controls.className = 'controls'; + controls.style.display = this._showOptionButtons ? 'block' : 'none'; + controls.appendChild(this.preserveCase.domNode); + + this.domNode.appendChild(controls); if (parent) { parent.appendChild(this.domNode); @@ -256,94 +341,6 @@ export class ReplaceInput extends Widget { dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); } - private buildDomNode(history: string[], flexibleHeight: boolean, flexibleWidth: boolean, flexibleMaxHeight: number | undefined): void { - this.domNode = document.createElement('div'); - dom.addClass(this.domNode, 'monaco-findInput'); - - this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, { - ariaLabel: this.label || '', - placeholder: this.placeholder || '', - validationOptions: { - validation: this.validation - }, - inputBackground: this.inputBackground, - inputForeground: this.inputForeground, - inputBorder: this.inputBorder, - inputValidationInfoBackground: this.inputValidationInfoBackground, - inputValidationInfoForeground: this.inputValidationInfoForeground, - inputValidationInfoBorder: this.inputValidationInfoBorder, - inputValidationWarningBackground: this.inputValidationWarningBackground, - inputValidationWarningForeground: this.inputValidationWarningForeground, - inputValidationWarningBorder: this.inputValidationWarningBorder, - inputValidationErrorBackground: this.inputValidationErrorBackground, - inputValidationErrorForeground: this.inputValidationErrorForeground, - inputValidationErrorBorder: this.inputValidationErrorBorder, - history, - flexibleHeight, - flexibleWidth, - flexibleMaxHeight - })); - - this.preserveCase = this._register(new PreserveCaseCheckbox({ - appendTitle: '', - isChecked: false, - inputActiveOptionBorder: this.inputActiveOptionBorder, - inputActiveOptionBackground: this.inputActiveOptionBackground, - })); - this._register(this.preserveCase.onChange(viaKeyboard => { - this._onDidOptionChange.fire(viaKeyboard); - if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { - this.inputBox.focus(); - } - this.validate(); - })); - this._register(this.preserveCase.onKeyDown(e => { - this._onPreserveCaseKeyDown.fire(e); - })); - - if (this._showOptionButtons) { - this.cachedOptionsWidth = this.preserveCase.width(); - } else { - this.cachedOptionsWidth = 0; - } - - // Arrow-Key support to navigate between options - let indexes = [this.preserveCase.domNode]; - this.onkeydown(this.domNode, (event: IKeyboardEvent) => { - if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) { - let index = indexes.indexOf(document.activeElement); - if (index >= 0) { - let newIndex: number = -1; - if (event.equals(KeyCode.RightArrow)) { - newIndex = (index + 1) % indexes.length; - } else if (event.equals(KeyCode.LeftArrow)) { - if (index === 0) { - newIndex = indexes.length - 1; - } else { - newIndex = index - 1; - } - } - - if (event.equals(KeyCode.Escape)) { - indexes[index].blur(); - } else if (newIndex >= 0) { - indexes[newIndex].focus(); - } - - dom.EventHelper.stop(event, true); - } - } - }); - - - let controls = document.createElement('div'); - controls.className = 'controls'; - controls.style.display = this._showOptionButtons ? 'block' : 'none'; - controls.appendChild(this.preserveCase.domNode); - - this.domNode.appendChild(controls); - } - public validate(): void { if (this.inputBox) { this.inputBox.validate(); diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 28b184f76d9..c2448a2fbff 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -9,7 +9,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, IGridViewOptions } from './gridview'; import { Event } from 'vs/base/common/event'; -import { InvisibleSizing } from 'vs/base/browser/ui/splitview/splitview'; export { Orientation, Sizing as GridViewSizing, IViewSize, orthogonal, LayoutPriority } from './gridview'; diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index e7dcf431b0d..cd2e4b3703d 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -15,15 +15,15 @@ export interface IHighlight { export class HighlightedLabel { private domNode: HTMLElement; - private text: string; - private title: string; - private highlights: IHighlight[]; - private didEverRender: boolean; + private text: string = ''; + private title: string = ''; + private highlights: IHighlight[] = []; + private didEverRender: boolean = false; constructor(container: HTMLElement, private supportOcticons: boolean) { this.domNode = document.createElement('span'); this.domNode.className = 'monaco-highlighted-label'; - this.didEverRender = false; + container.appendChild(this.domNode); } diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index b9fc4154234..0122e851bab 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -77,7 +77,7 @@ class FastLabelNode { } this._empty = empty; - this._element.style.marginLeft = empty ? '0' : null; + this._element.style.marginLeft = empty ? '0' : ''; } dispose(): void { diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index d7d3b1217b0..a3cf52ca023 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -351,7 +351,7 @@ export class InputBox extends Widget { } private updateScrollDimensions(): void { - if (typeof this.cachedContentHeight !== 'number' || typeof this.cachedHeight !== 'number') { + if (typeof this.cachedContentHeight !== 'number' || typeof this.cachedHeight !== 'number' || !this.scrollableElement) { return; } @@ -359,8 +359,8 @@ export class InputBox extends Widget { const height = this.cachedHeight; const scrollTop = this.input.scrollTop; - this.scrollableElement!.setScrollDimensions({ scrollHeight, height }); - this.scrollableElement!.setScrollPosition({ scrollTop }); + this.scrollableElement.setScrollDimensions({ scrollHeight, height }); + this.scrollableElement.setScrollPosition({ scrollTop }); } public showMessage(message: IMessage, force?: boolean): void { @@ -373,7 +373,7 @@ export class InputBox extends Widget { dom.addClass(this.element, this.classForType(message.type)); const styles = this.stylesForType(this.message.type); - this.element.style.border = styles.border ? `1px solid ${styles.border}` : null; + this.element.style.border = styles.border ? `1px solid ${styles.border}` : ''; // ARIA Support let alertText: string; @@ -473,9 +473,9 @@ export class InputBox extends Widget { dom.addClass(spanElement, this.classForType(this.message.type)); const styles = this.stylesForType(this.message.type); - spanElement.style.backgroundColor = styles.background ? styles.background.toString() : null; + spanElement.style.backgroundColor = styles.background ? styles.background.toString() : ''; spanElement.style.color = styles.foreground ? styles.foreground.toString() : null; - spanElement.style.border = styles.border ? `1px solid ${styles.border}` : null; + spanElement.style.border = styles.border ? `1px solid ${styles.border}` : ''; dom.append(div, spanElement); @@ -552,17 +552,17 @@ export class InputBox extends Widget { } protected applyStyles(): void { - const background = this.inputBackground ? this.inputBackground.toString() : null; - const foreground = this.inputForeground ? this.inputForeground.toString() : null; - const border = this.inputBorder ? this.inputBorder.toString() : null; + const background = this.inputBackground ? this.inputBackground.toString() : ''; + const foreground = this.inputForeground ? this.inputForeground.toString() : ''; + const border = this.inputBorder ? this.inputBorder.toString() : ''; this.element.style.backgroundColor = background; this.element.style.color = foreground; this.input.style.backgroundColor = background; this.input.style.color = foreground; - this.element.style.borderWidth = border ? '1px' : null; - this.element.style.borderStyle = border ? 'solid' : null; + this.element.style.borderWidth = border ? '1px' : ''; + this.element.style.borderStyle = border ? 'solid' : ''; this.element.style.borderColor = border; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 6f0ea59ff10..352dd6b8e34 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -155,6 +155,14 @@ export class PagedList implements IDisposable { this.list.scrollTop = scrollTop; } + get scrollLeft(): number { + return this.list.scrollLeft; + } + + set scrollLeft(scrollLeft: number) { + this.list.scrollLeft = scrollLeft; + } + open(indexes: number[], browserEvent?: UIEvent): void { this.list.open(indexes, browserEvent); } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 937fc0c870d..97ec6f37206 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -686,7 +686,7 @@ export class ListView implements ISpliceable, IDisposable { return scrollPosition.scrollLeft; } - setScrollLeftt(scrollLeft: number): void { + setScrollLeft(scrollLeft: number): void { if (this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable.dispose(); this.scrollableElementUpdateDisposable = null; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index a8a2a63da47..a70969206fb 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1316,7 +1316,7 @@ export class List implements ISpliceable, IDisposable { } set scrollLeft(scrollLeft: number) { - this.view.setScrollLeftt(scrollLeft); + this.view.setScrollLeft(scrollLeft); } get scrollHeight(): number { diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 131023cadad..4e94f810672 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -204,7 +204,7 @@ export class Menu extends ActionBar { })); const scrollElement = this.scrollableElement.getDomNode(); - scrollElement.style.position = null; + scrollElement.style.position = ''; menuElement.style.maxHeight = `${Math.max(10, window.innerHeight - container.getBoundingClientRect().top - 30)}px`; @@ -231,10 +231,10 @@ export class Menu extends ActionBar { style(style: IMenuStyles): void { const container = this.getContainer(); - const fgColor = style.foregroundColor ? `${style.foregroundColor}` : null; - const bgColor = style.backgroundColor ? `${style.backgroundColor}` : null; - const border = style.borderColor ? `2px solid ${style.borderColor}` : null; - const shadow = style.shadowColor ? `0 2px 4px ${style.shadowColor}` : null; + const fgColor = style.foregroundColor ? `${style.foregroundColor}` : ''; + const bgColor = style.backgroundColor ? `${style.backgroundColor}` : ''; + const border = style.borderColor ? `2px solid ${style.borderColor}` : ''; + const shadow = style.shadowColor ? `0 2px 4px ${style.shadowColor}` : ''; container.style.border = border; this.domNode.style.color = fgColor; @@ -398,7 +398,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { EventHelper.stop(e, true); this.onClick(e); })); - }, 50); + }, 100); this._register(this.runOnceToEnableMouseUp); } @@ -575,11 +575,11 @@ class BaseMenuActionViewItem extends BaseActionViewItem { const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; - const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : null; + const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : ''; this.item.style.color = fgColor ? `${fgColor}` : null; - this.check.style.backgroundColor = fgColor ? `${fgColor}` : null; - this.item.style.backgroundColor = bgColor ? `${bgColor}` : null; + this.check.style.backgroundColor = fgColor ? `${fgColor}` : ''; + this.item.style.backgroundColor = bgColor ? `${bgColor}` : ''; this.container.style.border = border; } @@ -793,7 +793,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; - this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : null; + this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : ''; if (this.parentData.submenu) { this.parentData.submenu.style(this.menuStyle); @@ -818,7 +818,9 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { class MenuSeparatorActionViewItem extends ActionViewItem { style(style: IMenuStyles): void { - this.label.style.borderBottomColor = style.separatorColor ? `${style.separatorColor}` : null; + if (this.label) { + this.label.style.borderBottomColor = style.separatorColor ? `${style.separatorColor}` : ''; + } } } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 3d3b3e5cb57..379da40e348 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -20,6 +20,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { asArray } from 'vs/base/common/arrays'; import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode'; import { isMacintosh } from 'vs/base/common/platform'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; const $ = DOM.$; @@ -252,7 +253,14 @@ export class MenuBar extends Disposable { e.stopPropagation(); })); - this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_DOWN, (e) => { + this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { + // Ignore non-left-click + const mouseEvent = new StandardMouseEvent(e); + if (!mouseEvent.leftButton) { + e.preventDefault(); + return; + } + if (!this.isOpen) { // Open the menu with mouse down and ignore the following mouse up event this.ignoreNextMouseUp = true; @@ -337,6 +345,13 @@ export class MenuBar extends Disposable { })); this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_DOWN, (e) => { + // Ignore non-left-click + const mouseEvent = new StandardMouseEvent(e); + if (!mouseEvent.leftButton) { + e.preventDefault(); + return; + } + if (!this.isOpen) { // Open the menu with mouse down and ignore the following mouse up event this.ignoreNextMouseUp = true; diff --git a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts index c5d77ed9724..209ba4133f5 100644 --- a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts +++ b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts @@ -8,7 +8,7 @@ import 'vs/css!./octicons/octicons-animations'; import { escape } from 'vs/base/common/strings'; function expand(text: string): string { - return text.replace(/\$\(((.+?)(~(.*?))?)\)/g, (_match, _g1, name, _g3, animation) => { + return text.replace(/\$\((([a-z0-9\-]+?)(~([a-z0-9\-]*?))?)\)/gi, (_match, _g1, name, _g3, animation) => { return ``; }); } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css index 2305d04890f..154a2251587 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css @@ -1,6 +1,6 @@ @font-face { font-family: "octicons"; - src: url("./octicons.ttf?628f71ee09945d25ba5fceb0c17f7b0f") format("truetype"); + src: url("./octicons.ttf?1829db8570ee0fa5a4bef3bb41d5f62e") format("truetype"); } .octicon, .mega-octicon { diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf index 09468d33244..90550092a65 100644 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf and b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf differ diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts index 405bfe80c05..198d8e2310e 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.ts +++ b/src/vs/base/browser/ui/progressbar/progressbar.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./progressbar'; -import * as assert from 'vs/base/common/assert'; import { Disposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -154,9 +153,7 @@ export class ProgressBar extends Disposable { * Tells the progress bar that an increment of work has been completed. */ worked(value: number): ProgressBar { - value = Number(value); - assert.ok(!isNaN(value), 'Value is not a number'); - value = Math.max(1, value); + value = Math.max(1, Number(value)); return this.doSetWorked(this.workedVal + value); } @@ -165,16 +162,13 @@ export class ProgressBar extends Disposable { * Tells the progress bar the total amount of work that has been completed. */ setWorked(value: number): ProgressBar { - value = Number(value); - assert.ok(!isNaN(value), 'Value is not a number'); - value = Math.max(1, value); + value = Math.max(1, Number(value)); return this.doSetWorked(value); } private doSetWorked(value: number): ProgressBar { - assert.ok(isNumber(this.totalWork), 'Total work not set'); - const totalWork = this.totalWork!; + const totalWork = this.totalWork || 100; this.workedVal = value; this.workedVal = Math.min(totalWork, this.workedVal); @@ -227,7 +221,7 @@ export class ProgressBar extends Disposable { protected applyStyles(): void { if (this.bit) { - const background = this.progressBarBackground ? this.progressBarBackground.toString() : null; + const background = this.progressBarBackground ? this.progressBarBackground.toString() : ''; this.bit.style.backgroundColor = background; } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index b5f2fe57c72..7e6ee0f7f6a 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -374,9 +374,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // Style parent select if (this.selectElement) { - const background = this.styles.selectBackground ? this.styles.selectBackground.toString() : null; - const foreground = this.styles.selectForeground ? this.styles.selectForeground.toString() : null; - const border = this.styles.selectBorder ? this.styles.selectBorder.toString() : null; + const background = this.styles.selectBackground ? this.styles.selectBackground.toString() : ''; + const foreground = this.styles.selectForeground ? this.styles.selectForeground.toString() : ''; + const border = this.styles.selectBorder ? this.styles.selectBorder.toString() : ''; this.selectElement.style.backgroundColor = background; this.selectElement.style.color = foreground; @@ -392,10 +392,10 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi private styleList() { if (this.selectList) { - let background = this.styles.selectBackground ? this.styles.selectBackground.toString() : null; + const background = this.styles.selectBackground ? this.styles.selectBackground.toString() : ''; this.selectList.style({}); - let listBackground = this.styles.selectListBackground ? this.styles.selectListBackground.toString() : background; + const listBackground = this.styles.selectListBackground ? this.styles.selectListBackground.toString() : background; this.selectDropDownListContainer.style.backgroundColor = listBackground; this.selectionDetailsPane.style.backgroundColor = listBackground; const optionsBorder = this.styles.focusBorder ? this.styles.focusBorder.toString() : ''; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index afaed056d4c..8c50b057fb5 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -147,9 +147,9 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { // Style native select if (this.selectElement) { - const background = this.styles.selectBackground ? this.styles.selectBackground.toString() : null; - const foreground = this.styles.selectForeground ? this.styles.selectForeground.toString() : null; - const border = this.styles.selectBorder ? this.styles.selectBorder.toString() : null; + const background = this.styles.selectBackground ? this.styles.selectBackground.toString() : ''; + const foreground = this.styles.selectForeground ? this.styles.selectForeground.toString() : ''; + const border = this.styles.selectBorder ? this.styles.selectBorder.toString() : ''; this.selectElement.style.backgroundColor = background; this.selectElement.style.color = foreground; diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 7c337dc62da..f7c63a466c6 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { $, append, addClass, removeClass, toggleClass, trackFocus, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { $, append, addClass, removeClass, toggleClass, trackFocus } from 'vs/base/browser/dom'; import { firstIndex } from 'vs/base/common/arrays'; import { Color, RGBA } from 'vs/base/common/color'; import { SplitView, IView } from './splitview'; @@ -58,6 +58,9 @@ export abstract class Panel extends Disposable implements IView { private readonly _onDidChange = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; + private readonly _onDidChangeExpansionState = this._register(new Emitter()); + readonly onDidChangeExpansionState: Event = this._onDidChangeExpansionState.event; + get draggableElement(): HTMLElement { return this.header; } @@ -144,6 +147,7 @@ export abstract class Panel extends Disposable implements IView { }, 200); } + this._onDidChangeExpansionState.fire(expanded); this._onDidChange.fire(expanded ? this.expandedSize : undefined); return true; } @@ -225,8 +229,8 @@ export abstract class Panel extends Disposable implements IView { this.header.setAttribute('aria-expanded', String(expanded)); this.header.style.color = this.styles.headerForeground ? this.styles.headerForeground.toString() : null; - this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : null; - this.header.style.borderTop = this.styles.headerBorder ? `1px solid ${this.styles.headerBorder}` : null; + this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : ''; + this.header.style.borderTop = this.styles.headerBorder ? `1px solid ${this.styles.headerBorder}` : ''; this._dropBackground = this.styles.dropBackground; } @@ -336,7 +340,7 @@ class PanelDraggable extends Disposable { backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString(); } - this.panel.dropTargetElement.style.backgroundColor = backgroundColor; + this.panel.dropTargetElement.style.backgroundColor = backgroundColor || ''; } } @@ -391,13 +395,7 @@ export class PanelView extends Disposable { addPanel(panel: Panel, size: number, index = this.splitview.length): void { const disposables = new DisposableStore(); - - // https://github.com/Microsoft/vscode/issues/59950 - let shouldAnimate = false; - disposables.add(scheduleAtNextAnimationFrame(() => shouldAnimate = true)); - - disposables.add(Event.filter(panel.onDidChange, () => shouldAnimate) - (this.setupAnimation, this)); + panel.onDidChangeExpansionState(this.setupAnimation, this, disposables); const panelItem = { panel, disposable: disposables }; this.panelItems.splice(index, 0, panelItem); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index fd67ae50931..b34dd3936a7 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -238,7 +238,7 @@ class EventCollection implements Collection { class TreeRenderer implements IListRenderer, ITreeListTemplateData> { - private static DefaultIndent = 8; + private static readonly DefaultIndent = 8; readonly templateId: string; private renderedElements = new Map>(); @@ -780,8 +780,8 @@ class TypeFilterController implements IDisposable { const onDragEnd = () => { this.positionClassName = positionClassName; this.domNode.className = `monaco-list-type-filter ${this.positionClassName}`; - this.domNode.style.top = null; - this.domNode.style.left = null; + this.domNode.style.top = ''; + this.domNode.style.left = ''; dispose(disposables); }; @@ -855,7 +855,13 @@ class TypeFilterController implements IDisposable { } dispose() { - this.disable(); + if (this._enabled) { + this.domNode.remove(); + this.enabledDisposables = dispose(this.enabledDisposables); + this._enabled = false; + this.triggered = false; + } + this._onDidChangePattern.dispose(); this.disposables = dispose(this.disposables); } @@ -1362,7 +1368,7 @@ export abstract class AbstractTree implements IDisposable } get scrollLeft(): number { - return this.view.scrollTop; + return this.view.scrollLeft; } set scrollLeft(scrollLeft: number) { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 4cf442d26df..e7514bfabe6 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -7,7 +7,7 @@ import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper } from 'vs/base/browser/ui/tree/tree'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; @@ -88,7 +88,6 @@ class AsyncDataTreeRenderer implements IT readonly templateId: string; private renderedNodes = new Map, IDataTreeListTemplateData>(); - private disposables: IDisposable[] = []; constructor( protected renderer: ITreeRenderer, @@ -124,7 +123,6 @@ class AsyncDataTreeRenderer implements IT dispose(): void { this.renderedNodes.clear(); - this.disposables = dispose(this.disposables); } } @@ -292,7 +290,7 @@ export class AsyncDataTree implements IDisposable protected readonly nodeMapper: AsyncDataTreeNodeMapper = new WeakMapper(node => new AsyncDataTreeNodeWrapper(node)); - protected readonly disposables: IDisposable[] = []; + protected readonly disposables = new DisposableStore(); get onDidScroll(): Event { return this.tree.onDidScroll; } @@ -930,7 +928,7 @@ export class AsyncDataTree implements IDisposable } dispose(): void { - dispose(this.disposables); + this.disposables.dispose(); } } diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index eff86b6ab5e..b168e7e14ce 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -14,7 +14,7 @@ import { CompressibleObjectTreeModel, ElementMapper, ICompressedTreeNode, ICompr import { memoize } from 'vs/base/common/decorators'; export interface IObjectTreeOptions extends IAbstractTreeOptions { - sorter?: ITreeSorter; + readonly sorter?: ITreeSorter; } export class ObjectTree, TFilterData = void> extends AbstractTree { @@ -59,10 +59,6 @@ interface ICompressedTreeNodeProvider { getCompressedTreeNode(element: T): ITreeNode, TFilterData>; } -export interface ICompressibleObjectTreeOptions extends IObjectTreeOptions { - readonly elementMapper?: ElementMapper; -} - export interface ICompressibleTreeRenderer extends ITreeRenderer { renderCompressedElements(node: ITreeNode, TFilterData>, index: number, templateData: TTemplateData, height: number | undefined): void; disposeCompressedElements?(node: ITreeNode, TFilterData>, index: number, templateData: TTemplateData, height: number | undefined): void; @@ -136,6 +132,7 @@ export interface ICompressibleKeyboardNavigationLabelProvider extends IKeyboa } export interface ICompressibleObjectTreeOptions extends IObjectTreeOptions { + readonly elementMapper?: ElementMapper; readonly keyboardNavigationLabelProvider?: ICompressibleKeyboardNavigationLabelProvider; } @@ -164,7 +161,7 @@ function asObjectTreeOptions(compressedTreeNodeProvider: () => I export class CompressibleObjectTree, TFilterData = void> extends ObjectTree implements ICompressedTreeNodeProvider { - protected model: CompressibleObjectTreeModel; + protected model!: CompressibleObjectTreeModel; constructor( user: string, diff --git a/src/vs/base/common/cancellation.ts b/src/vs/base/common/cancellation.ts index fb274fb941f..f741f6b7af1 100644 --- a/src/vs/base/common/cancellation.ts +++ b/src/vs/base/common/cancellation.ts @@ -116,7 +116,10 @@ export class CancellationTokenSource { } } - dispose(): void { + dispose(cancel: boolean = false): void { + if (cancel) { + this.cancel(); + } if (this._parentListener) { this._parentListener.dispose(); } diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index 97528a4534c..8068e76aadd 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -507,10 +507,6 @@ export namespace Color { * The default format will use HEX if opaque and RGBA otherwise. */ export function format(color: Color): string | null { - if (!color) { - return null; - } - if (color.isOpaque()) { return Color.Format.CSS.formatHex(color); } @@ -524,11 +520,6 @@ export namespace Color { * @param hex string (#RGB, #RGBA, #RRGGBB or #RRGGBBAA). */ export function parseHex(hex: string): Color | null { - if (!hex) { - // Invalid color - return null; - } - const length = hex.length; if (length === 0) { diff --git a/src/vs/base/common/diff/diff.ts b/src/vs/base/common/diff/diff.ts index 6eb26b885af..abe78fbd8be 100644 --- a/src/vs/base/common/diff/diff.ts +++ b/src/vs/base/common/diff/diff.ts @@ -4,22 +4,29 @@ *--------------------------------------------------------------------------------------------*/ import { DiffChange } from 'vs/base/common/diff/diffChange'; +import { stringHash } from 'vs/base/common/hash'; +import { Constants } from 'vs/base/common/uint'; -function createStringSequence(a: string): ISequence { - return { - getLength() { return a.length; }, - getElementAtIndex(pos: number) { return a.charCodeAt(pos); } - }; +export class StringDiffSequence implements ISequence { + + constructor(private source: string) { } + + getElements(): Int32Array | number[] | string[] { + const source = this.source; + const characters = new Int32Array(source.length); + for (let i = 0, len = source.length; i < len; i++) { + characters[i] = source.charCodeAt(i); + } + return characters; + } } export function stringDiff(original: string, modified: string, pretty: boolean): IDiffChange[] { - return new LcsDiff(createStringSequence(original), createStringSequence(modified)).ComputeDiff(pretty); + return new LcsDiff(new StringDiffSequence(original), new StringDiffSequence(modified)).ComputeDiff(pretty); } - export interface ISequence { - getLength(): number; - getElementAtIndex(index: number): number | string; + getElements(): Int32Array | number[] | string[]; } export interface IDiffChange { @@ -49,7 +56,7 @@ export interface IDiffChange { } export interface IContinueProcessingPredicate { - (furthestOriginalIndex: number, originalSequence: ISequence, matchLengthOfLongest: number): boolean; + (furthestOriginalIndex: number, matchLengthOfLongest: number): boolean; } // @@ -86,6 +93,11 @@ export class MyArray { destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i]; } } + public static Copy2(sourceArray: Int32Array, sourceIndex: number, destinationArray: Int32Array, destinationIndex: number, length: number) { + for (let i = 0; i < length; i++) { + destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i]; + } + } } //***************************************************************************** @@ -127,8 +139,8 @@ class DiffChangeHelper { */ constructor() { this.m_changes = []; - this.m_originalStart = Number.MAX_VALUE; - this.m_modifiedStart = Number.MAX_VALUE; + this.m_originalStart = Constants.MAX_SAFE_SMALL_INTEGER; + this.m_modifiedStart = Constants.MAX_SAFE_SMALL_INTEGER; this.m_originalCount = 0; this.m_modifiedCount = 0; } @@ -147,8 +159,8 @@ class DiffChangeHelper { // Reset for the next change this.m_originalCount = 0; this.m_modifiedCount = 0; - this.m_originalStart = Number.MAX_VALUE; - this.m_modifiedStart = Number.MAX_VALUE; + this.m_originalStart = Constants.MAX_SAFE_SMALL_INTEGER; + this.m_modifiedStart = Constants.MAX_SAFE_SMALL_INTEGER; } /** @@ -214,39 +226,81 @@ class DiffChangeHelper { */ export class LcsDiff { - private OriginalSequence: ISequence; - private ModifiedSequence: ISequence; - private ContinueProcessingPredicate: IContinueProcessingPredicate | null; + private readonly ContinueProcessingPredicate: IContinueProcessingPredicate | null; - private m_forwardHistory: number[][]; - private m_reverseHistory: number[][]; + private readonly _hasStrings: boolean; + private readonly _originalStringElements: string[]; + private readonly _originalElementsOrHash: Int32Array; + private readonly _modifiedStringElements: string[]; + private readonly _modifiedElementsOrHash: Int32Array; + + private m_forwardHistory: Int32Array[]; + private m_reverseHistory: Int32Array[]; /** * Constructs the DiffFinder */ - constructor(originalSequence: ISequence, newSequence: ISequence, continueProcessingPredicate: IContinueProcessingPredicate | null = null) { - this.OriginalSequence = originalSequence; - this.ModifiedSequence = newSequence; + constructor(originalSequence: ISequence, modifiedSequence: ISequence, continueProcessingPredicate: IContinueProcessingPredicate | null = null) { this.ContinueProcessingPredicate = continueProcessingPredicate; + const [originalStringElements, originalElementsOrHash, originalHasStrings] = LcsDiff._getElements(originalSequence); + const [modifiedStringElements, modifiedElementsOrHash, modifiedHasStrings] = LcsDiff._getElements(modifiedSequence); + + this._hasStrings = (originalHasStrings && modifiedHasStrings); + this._originalStringElements = originalStringElements; + this._originalElementsOrHash = originalElementsOrHash; + this._modifiedStringElements = modifiedStringElements; + this._modifiedElementsOrHash = modifiedElementsOrHash; + this.m_forwardHistory = []; this.m_reverseHistory = []; } + private static _isStringArray(arr: Int32Array | number[] | string[]): arr is string[] { + return (arr.length > 0 && typeof arr[0] === 'string'); + } + + private static _getElements(sequence: ISequence): [string[], Int32Array, boolean] { + const elements = sequence.getElements(); + + if (LcsDiff._isStringArray(elements)) { + const hashes = new Int32Array(elements.length); + for (let i = 0, len = elements.length; i < len; i++) { + hashes[i] = stringHash(elements[i], 0); + } + return [elements, hashes, true]; + } + + if (elements instanceof Int32Array) { + return [[], elements, false]; + } + + return [[], new Int32Array(elements), false]; + } + private ElementsAreEqual(originalIndex: number, newIndex: number): boolean { - return (this.OriginalSequence.getElementAtIndex(originalIndex) === this.ModifiedSequence.getElementAtIndex(newIndex)); + if (this._originalElementsOrHash[originalIndex] !== this._modifiedElementsOrHash[newIndex]) { + return false; + } + return (this._hasStrings ? this._originalStringElements[originalIndex] === this._modifiedStringElements[newIndex] : true); } private OriginalElementsAreEqual(index1: number, index2: number): boolean { - return (this.OriginalSequence.getElementAtIndex(index1) === this.OriginalSequence.getElementAtIndex(index2)); + if (this._originalElementsOrHash[index1] !== this._originalElementsOrHash[index2]) { + return false; + } + return (this._hasStrings ? this._originalStringElements[index1] === this._originalStringElements[index2] : true); } private ModifiedElementsAreEqual(index1: number, index2: number): boolean { - return (this.ModifiedSequence.getElementAtIndex(index1) === this.ModifiedSequence.getElementAtIndex(index2)); + if (this._modifiedElementsOrHash[index1] !== this._modifiedElementsOrHash[index2]) { + return false; + } + return (this._hasStrings ? this._modifiedStringElements[index1] === this._modifiedStringElements[index2] : true); } public ComputeDiff(pretty: boolean): IDiffChange[] { - return this._ComputeDiff(0, this.OriginalSequence.getLength() - 1, 0, this.ModifiedSequence.getLength() - 1, pretty); + return this._ComputeDiff(0, this._originalElementsOrHash.length - 1, 0, this._modifiedElementsOrHash.length - 1, pretty); } /** @@ -358,7 +412,7 @@ export class LcsDiff { private WALKTRACE(diagonalForwardBase: number, diagonalForwardStart: number, diagonalForwardEnd: number, diagonalForwardOffset: number, diagonalReverseBase: number, diagonalReverseStart: number, diagonalReverseEnd: number, diagonalReverseOffset: number, - forwardPoints: number[], reversePoints: number[], + forwardPoints: Int32Array, reversePoints: Int32Array, originalIndex: number, originalEnd: number, midOriginalArr: number[], modifiedIndex: number, modifiedEnd: number, midModifiedArr: number[], deltaIsEven: boolean, quitEarlyArr: boolean[]): DiffChange[] { @@ -369,7 +423,7 @@ export class LcsDiff { let diagonalMin = diagonalForwardStart; let diagonalMax = diagonalForwardEnd; let diagonalRelative = (midOriginalArr[0] - midModifiedArr[0]) - diagonalForwardOffset; - let lastOriginalIndex = Number.MIN_VALUE; + let lastOriginalIndex = Constants.MIN_SAFE_SMALL_INTEGER; let historyIndex = this.m_forwardHistory.length - 1; let diagonal: number; @@ -435,7 +489,7 @@ export class LcsDiff { diagonalMin = diagonalReverseStart; diagonalMax = diagonalReverseEnd; diagonalRelative = (midOriginalArr[0] - midModifiedArr[0]) - diagonalReverseOffset; - lastOriginalIndex = Number.MAX_VALUE; + lastOriginalIndex = Constants.MAX_SAFE_SMALL_INTEGER; historyIndex = (deltaIsEven) ? this.m_reverseHistory.length - 1 : this.m_reverseHistory.length - 2; do { @@ -523,8 +577,8 @@ export class LcsDiff { // The modifiedIndex can be computed mathematically from the originalIndex and the diagonal number. let maxDifferences = (originalEnd - originalStart) + (modifiedEnd - modifiedStart); let numDiagonals = maxDifferences + 1; - let forwardPoints: number[] = new Array(numDiagonals); - let reversePoints: number[] = new Array(numDiagonals); + let forwardPoints = new Int32Array(numDiagonals); + let reversePoints = new Int32Array(numDiagonals); // diagonalForwardBase: Index into forwardPoints of the diagonal which passes through (originalStart, modifiedStart) // diagonalReverseBase: Index into reversePoints of the diagonal which passes through (originalEnd, modifiedEnd) let diagonalForwardBase = (modifiedEnd - modifiedStart); @@ -624,7 +678,7 @@ export class LcsDiff { // Check to see if we should be quitting early, before moving on to the next iteration. let matchLengthOfLongest = ((furthestOriginalIndex - originalStart) + (furthestModifiedIndex - modifiedStart) - numDifferences) / 2; - if (this.ContinueProcessingPredicate !== null && !this.ContinueProcessingPredicate(furthestOriginalIndex, this.OriginalSequence, matchLengthOfLongest)) { + if (this.ContinueProcessingPredicate !== null && !this.ContinueProcessingPredicate(furthestOriginalIndex, matchLengthOfLongest)) { // We can't finish, so skip ahead to generating a result from what we have. quitEarlyArr[0] = true; @@ -711,14 +765,14 @@ export class LcsDiff { if (numDifferences <= MaxDifferencesHistory) { // We are allocating space for one extra int, which we fill with // the index of the diagonal base index - let temp: number[] = new Array(diagonalForwardEnd - diagonalForwardStart + 2); + let temp = new Int32Array(diagonalForwardEnd - diagonalForwardStart + 2); temp[0] = diagonalForwardBase - diagonalForwardStart + 1; - MyArray.Copy(forwardPoints, diagonalForwardStart, temp, 1, diagonalForwardEnd - diagonalForwardStart + 1); + MyArray.Copy2(forwardPoints, diagonalForwardStart, temp, 1, diagonalForwardEnd - diagonalForwardStart + 1); this.m_forwardHistory.push(temp); - temp = new Array(diagonalReverseEnd - diagonalReverseStart + 2); + temp = new Int32Array(diagonalReverseEnd - diagonalReverseStart + 2); temp[0] = diagonalReverseBase - diagonalReverseStart + 1; - MyArray.Copy(reversePoints, diagonalReverseStart, temp, 1, diagonalReverseEnd - diagonalReverseStart + 1); + MyArray.Copy2(reversePoints, diagonalReverseStart, temp, 1, diagonalReverseEnd - diagonalReverseStart + 1); this.m_reverseHistory.push(temp); } @@ -750,8 +804,8 @@ export class LcsDiff { // Shift all the changes down first for (let i = 0; i < changes.length; i++) { const change = changes[i]; - const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this.OriginalSequence.getLength(); - const modifiedStop = (i < changes.length - 1) ? changes[i + 1].modifiedStart : this.ModifiedSequence.getLength(); + const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this._originalElementsOrHash.length; + const modifiedStop = (i < changes.length - 1) ? changes[i + 1].modifiedStart : this._modifiedElementsOrHash.length; const checkOriginal = change.originalLength > 0; const checkModified = change.modifiedLength > 0; @@ -826,11 +880,10 @@ export class LcsDiff { } private _OriginalIsBoundary(index: number): boolean { - if (index <= 0 || index >= this.OriginalSequence.getLength() - 1) { + if (index <= 0 || index >= this._originalElementsOrHash.length - 1) { return true; } - const element = this.OriginalSequence.getElementAtIndex(index); - return (typeof element === 'string' && /^\s*$/.test(element)); + return (this._hasStrings && /^\s*$/.test(this._originalStringElements[index])); } private _OriginalRegionIsBoundary(originalStart: number, originalLength: number): boolean { @@ -847,11 +900,10 @@ export class LcsDiff { } private _ModifiedIsBoundary(index: number): boolean { - if (index <= 0 || index >= this.ModifiedSequence.getLength() - 1) { + if (index <= 0 || index >= this._modifiedElementsOrHash.length - 1) { return true; } - const element = this.ModifiedSequence.getElementAtIndex(index); - return (typeof element === 'string' && /^\s*$/.test(element)); + return (this._hasStrings && /^\s*$/.test(this._modifiedStringElements[index])); } private _ModifiedRegionIsBoundary(modifiedStart: number, modifiedLength: number): boolean { diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 0a5106d3bd6..1902e82c312 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -36,7 +36,7 @@ function booleanHash(b: boolean, initialHashVal: number): number { return numberHash(b ? 433 : 863, initialHashVal); } -function stringHash(s: string, hashVal: number) { +export function stringHash(s: string, hashVal: number) { hashVal = numberHash(149417, hashVal); for (let i = 0, length = s.length; i < length; i++) { hashVal = numberHash(s.charCodeAt(i), hashVal); diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index ac7c8ed3a91..c9bfed7ea8f 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -7,37 +7,44 @@ import { equals } from 'vs/base/common/arrays'; import { UriComponents } from 'vs/base/common/uri'; export interface IMarkdownString { - value: string; - isTrusted?: boolean; + readonly value: string; + readonly isTrusted?: boolean; uris?: { [href: string]: UriComponents }; } export class MarkdownString implements IMarkdownString { - value: string; - isTrusted?: boolean; + private _value: string; + private _isTrusted: boolean; - constructor(value: string = '') { - this.value = value; + constructor(value: string = '', isTrusted = false) { + this._value = value; + this._isTrusted = isTrusted; } + get value() { return this._value; } + get isTrusted() { return this._isTrusted; } + appendText(value: string): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this.value += value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); + this._value += value + .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') + .replace('\n', '\n\n'); + return this; } appendMarkdown(value: string): MarkdownString { - this.value += value; + this._value += value; return this; } appendCodeblock(langId: string, code: string): MarkdownString { - this.value += '\n```'; - this.value += langId; - this.value += '\n'; - this.value += code; - this.value += '\n```\n'; + this._value += '\n```'; + this._value += langId; + this._value += '\n'; + this._value += code; + this._value += '\n```\n'; return this; } } diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts index ec08bea7e91..98ac19e0843 100644 --- a/src/vs/base/common/keyCodes.ts +++ b/src/vs/base/common/keyCodes.ts @@ -5,6 +5,7 @@ import { OperatingSystem } from 'vs/base/common/platform'; import { illegalArgument } from 'vs/base/common/errors'; +import { equals } from 'vs/base/common/arrays'; /** * Virtual Key Codes, the value does not hold any inherent meaning. @@ -525,15 +526,7 @@ export class ChordKeybinding { if (other === null) { return false; } - if (this.parts.length !== other.parts.length) { - return false; - } - for (let i = 0; i < this.parts.length; i++) { - if (!this.parts[i].equals(other.parts[i])) { - return false; - } - } - return true; + return equals(this.parts, other.parts, (a, b) => a.equals(b)); } } diff --git a/src/vs/base/common/lazy.ts b/src/vs/base/common/lazy.ts new file mode 100644 index 00000000000..fe88e82bc80 --- /dev/null +++ b/src/vs/base/common/lazy.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * A value that is resolved synchronously when it is first needed. + */ +export interface Lazy { + + hasValue(): boolean; + + + getValue(): T; + + + map(f: (x: T) => R): Lazy; +} + +export class Lazy { + + private _didRun: boolean = false; + private _value?: T; + private _error: any; + + constructor( + private readonly executor: () => T, + ) { } + + /** + * True if the lazy value has been resolved. + */ + hasValue() { return this._didRun; } + + /** + * Get the wrapped value. + * + * This will force evaluation of the lazy value if it has not been resolved yet. Lazy values are only + * resolved once. `getValue` will re-throw exceptions that are hit while resolving the value + */ + getValue(): T { + if (!this._didRun) { + try { + this._value = this.executor(); + } catch (err) { + this._error = err; + } finally { + this._didRun = true; + } + } + if (this._error) { + throw this._error; + } + return this._value!; + } + + /** + * Create a new lazy value that is the result of applying `f` to the wrapped value. + * + * This does not force the evaluation of the current lazy value. + */ + map(f: (x: T) => R): Lazy { + return new Lazy(() => f(this.getValue())); + } +} diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 44cfdada8ab..8d1cbd5b996 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -138,7 +138,7 @@ export class DisposableStore implements IDisposable { export abstract class Disposable implements IDisposable { - static None = Object.freeze({ dispose() { } }); + static readonly None = Object.freeze({ dispose() { } }); private readonly _store = new DisposableStore(); diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 007d0a0d529..dadb16862bb 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -117,6 +117,8 @@ export class PathIterator implements IKeyIterator { private _from!: number; private _to!: number; + constructor(private _splitOnBackslash: boolean = true) { } + reset(key: string): this { this._value = key.replace(/\\$|\/$/, ''); this._from = 0; @@ -134,7 +136,7 @@ export class PathIterator implements IKeyIterator { let justSeps = true; for (; this._to < this._value.length; this._to++) { const ch = this._value.charCodeAt(this._to); - if (ch === CharCode.Slash || ch === CharCode.Backslash) { + if (ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash) { if (justSeps) { this._from++; } else { diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 7d525614421..70093426625 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -92,7 +92,10 @@ class RemoteAuthoritiesImpl { return this._delegate(uri); } const authority = uri.authority; - const host = this._hosts[authority]; + let host = this._hosts[authority]; + if (host.indexOf(':') !== -1) { + host = `[${host}]`; + } const port = this._ports[authority]; const connectionToken = this._connectionTokens[authority]; return URI.from({ diff --git a/src/vs/base/common/octicon.ts b/src/vs/base/common/octicon.ts index 48da86f6cc1..80b5653f32e 100644 --- a/src/vs/base/common/octicon.ts +++ b/src/vs/base/common/octicon.ts @@ -9,8 +9,8 @@ import { ltrim } from 'vs/base/common/strings'; const octiconStartMarker = '$('; export interface IParsedOcticons { - text: string; - octiconOffsets?: number[]; + readonly text: string; + readonly octiconOffsets?: readonly number[]; } export function parseOcticons(text: string): IParsedOcticons { @@ -78,7 +78,16 @@ function doParseOcticons(text: string, firstOcticonIndex: number): IParsedOctico // within octicon else if (currentOcticonStart !== -1) { - currentOcticonValue += char; + // Make sure this is a real octicon name + if (/^[a-z0-9\-]$/i.test(char)) { + currentOcticonValue += char; + } else { + // This is not a real octicon, treat it as text + appendChars(currentOcticonValue); + + currentOcticonStart = -1; + currentOcticonValue = ''; + } } // any value outside of octicons @@ -123,4 +132,4 @@ export function matchesFuzzyOcticonAware(query: string, target: IParsedOcticons, } return matches; -} \ No newline at end of file +} diff --git a/src/vs/base/common/resourceTree.ts b/src/vs/base/common/resourceTree.ts index 98c0f518d73..af53307e036 100644 --- a/src/vs/base/common/resourceTree.ts +++ b/src/vs/base/common/resourceTree.ts @@ -9,6 +9,7 @@ import { Iterator } from 'vs/base/common/iterator'; import { relativePath, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { mapValues } from 'vs/base/common/collections'; +import { PathIterator } from 'vs/base/common/map'; export interface ILeafNode { readonly uri: URI; @@ -68,6 +69,10 @@ class BranchNode extends Node implements IBranchNode { delete(path: string): void { this._children.delete(path); } + + clear(): void { + this._children.clear(); + } } class LeafNode extends Node implements ILeafNode { @@ -113,18 +118,18 @@ export class ResourceTree, C> { add(uri: URI, element: T): void { const key = relativePath(this.root.uri, uri) || uri.fsPath; - const parts = key.split(/[\\\/]/).filter(p => !!p); + const iterator = new PathIterator(false).reset(key); let node = this.root; let path = ''; - for (let i = 0; i < parts.length; i++) { - const name = parts[i]; + while (true) { + const name = iterator.value(); path = path + '/' + name; let child = node.get(name); if (!child) { - if (i < parts.length - 1) { + if (iterator.hasNext()) { child = new BranchNode(joinPath(this.root.uri, path), path, this.root.context, node); node.set(name, child); } else { @@ -135,29 +140,35 @@ export class ResourceTree, C> { } if (!(child instanceof BranchNode)) { - if (i < parts.length - 1) { + if (iterator.hasNext()) { throw new Error('Inconsistent tree: can\'t override leaf with branch.'); } // replace node.set(name, new LeafNode(uri, path, this.root.context, element)); return; - } else if (i === parts.length - 1) { + } else if (!iterator.hasNext()) { throw new Error('Inconsistent tree: can\'t override branch with leaf.'); } node = child; + + if (!iterator.hasNext()) { + return; + } + + iterator.next(); } } delete(uri: URI): T | undefined { const key = relativePath(this.root.uri, uri) || uri.fsPath; - const parts = key.split(/[\\\/]/).filter(p => !!p); - return this._delete(this.root, parts, 0); + const iterator = new PathIterator(false).reset(key); + return this._delete(this.root, iterator); } - private _delete(node: BranchNode, parts: string[], index: number): T | undefined { - const name = parts[index]; + private _delete(node: BranchNode, iterator: PathIterator): T | undefined { + const name = iterator.value(); const child = node.get(name); if (!child) { @@ -165,9 +176,9 @@ export class ResourceTree, C> { } // not at end - if (index < parts.length - 1) { + if (iterator.hasNext()) { if (child instanceof BranchNode) { - const result = this._delete(child, parts, index + 1); + const result = this._delete(child, iterator.next()); if (typeof result !== 'undefined' && child.size === 0) { node.delete(name); @@ -188,4 +199,8 @@ export class ResourceTree, C> { node.delete(name); return child.element; } + + clear(): void { + this.root.clear(); + } } diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts index d92882578b7..7b57f1e5a66 100644 --- a/src/vs/base/common/search.ts +++ b/src/vs/base/common/search.ts @@ -7,20 +7,19 @@ import * as strings from './strings'; export function buildReplaceStringWithCasePreserved(matches: string[] | null, pattern: string): string { if (matches && (matches[0] !== '')) { + const containsHyphens = validateSpecificSpecialCharacter(matches, pattern, '-'); + const containsUnderscores = validateSpecificSpecialCharacter(matches, pattern, '_'); + if (containsHyphens && !containsUnderscores) { + return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '-'); + } else if (!containsHyphens && containsUnderscores) { + return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '_'); + } if (matches[0].toUpperCase() === matches[0]) { return pattern.toUpperCase(); } else if (matches[0].toLowerCase() === matches[0]) { return pattern.toLowerCase(); } else if (strings.containsUppercaseCharacter(matches[0][0])) { - const containsHyphens = validateSpecificSpecialCharacter(matches, pattern, '-'); - const containsUnderscores = validateSpecificSpecialCharacter(matches, pattern, '_'); - if (containsHyphens && !containsUnderscores) { - return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '-'); - } else if (!containsHyphens && containsUnderscores) { - return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '_'); - } else { - return pattern[0].toUpperCase() + pattern.substr(1); - } + return pattern[0].toUpperCase() + pattern.substr(1); } else { // we don't understand its pattern yet. return pattern; diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 2ce9e0eb47c..f464f9a0cb4 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -93,6 +93,39 @@ export function isUndefinedOrNull(obj: any): obj is undefined | null { return isUndefined(obj) || obj === null; } +/** + * Asserts that the argument passed in is neither undefined nor null. + */ +export function assertIsDefined(arg: T | null | undefined): T { + if (isUndefinedOrNull(arg)) { + throw new Error('Assertion Failed: argument is undefined or null'); + } + + return arg; +} + +/** + * Asserts that each argument passed in is neither undefined nor null. + */ +export function assertAllDefined(t1: T1 | null | undefined, t2: T2 | null | undefined): [T1, T2]; +export function assertAllDefined(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined): [T1, T2, T3]; +export function assertAllDefined(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined, t4: T4 | null | undefined): [T1, T2, T3, T4]; +export function assertAllDefined(...args: (unknown | null | undefined)[]): unknown[] { + const result = []; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (isUndefinedOrNull(arg)) { + throw new Error(`Assertion Failed: argument at index ${i} is undefined or null`); + } + + result.push(arg); + } + + return result; +} + const hasOwnProperty = Object.prototype.hasOwnProperty; /** diff --git a/src/vs/editor/common/core/uint.ts b/src/vs/base/common/uint.ts similarity index 74% rename from src/vs/editor/common/core/uint.ts rename to src/vs/base/common/uint.ts index 00e3b872903..0f5a4f8930a 100644 --- a/src/vs/editor/common/core/uint.ts +++ b/src/vs/base/common/uint.ts @@ -3,32 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export class Uint8Matrix { - - private readonly _data: Uint8Array; - public readonly rows: number; - public readonly cols: number; - - constructor(rows: number, cols: number, defaultValue: number) { - const data = new Uint8Array(rows * cols); - for (let i = 0, len = rows * cols; i < len; i++) { - data[i] = defaultValue; - } - - this._data = data; - this.rows = rows; - this.cols = cols; - } - - public get(row: number, col: number): number { - return this._data[row * this.cols + col]; - } - - public set(row: number, col: number, value: number): void { - this._data[row * this.cols + col] = value; - } -} - export const enum Constants { /** * MAX SMI (SMall Integer) as defined in v8. diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 83428b3590c..f079038301c 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -122,7 +122,11 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions // detection. thus, wrap up starting the stream even // without all the data to get things going else { - this._startDecodeStream(() => this.decodeStream!.end(callback)); + this._startDecodeStream(() => { + if (this.decodeStream) { + this.decodeStream.end(callback); + } + }); } } }; diff --git a/src/vs/base/node/languagePacks.d.ts b/src/vs/base/node/languagePacks.d.ts index 241392e5eca..5c69043ef23 100644 --- a/src/vs/base/node/languagePacks.d.ts +++ b/src/vs/base/node/languagePacks.d.ts @@ -9,6 +9,7 @@ export interface NLSConfiguration { [key: string]: string; }; pseudo?: boolean; + _languagePackSupport?: boolean; } export interface InternalNLSConfiguration extends NLSConfiguration { @@ -20,4 +21,4 @@ export interface InternalNLSConfiguration extends NLSConfiguration { _languagePackSupport?: boolean; } -export function getNLSConfiguration(commit: string, userDataPath: string, metaDataFile: string, locale: string): Promise; \ No newline at end of file +export function getNLSConfiguration(commit: string, userDataPath: string, metaDataFile: string, locale: string): Promise; diff --git a/src/vs/base/node/macAddress.ts b/src/vs/base/node/macAddress.ts index dd36be22344..524b136c06b 100644 --- a/src/vs/base/node/macAddress.ts +++ b/src/vs/base/node/macAddress.ts @@ -11,21 +11,15 @@ const cmdline = { unix: '/sbin/ifconfig -a || /sbin/ip link' }; -const invalidMacAddresses = [ +const invalidMacAddresses = new Set([ '00:00:00:00:00:00', 'ff:ff:ff:ff:ff:ff', 'ac:de:48:00:11:22' -]; +]); function validateMacAddress(candidate: string): boolean { - let tempCandidate = candidate.replace(/\-/g, ':').toLowerCase(); - for (let invalidMacAddress of invalidMacAddresses) { - if (invalidMacAddress === tempCandidate) { - return false; - } - } - - return true; + const tempCandidate = candidate.replace(/\-/g, ':').toLowerCase(); + return !invalidMacAddresses.has(tempCandidate); } export function getMac(): Promise { @@ -66,4 +60,4 @@ function doGetMac(): Promise { reject(err); } }); -} \ No newline at end of file +} diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 14dca3d5f49..e08289bdce4 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -679,4 +679,4 @@ const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE; -export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; \ No newline at end of file +export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 5bee9f9eced..6b3376ec4f1 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -122,6 +122,7 @@ export abstract class AbstractProcess { } this.childProcess = null; + this.childProcessPromise = null; this.terminateRequested = false; if (this.options.env) { diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index 3c474dc0343..17080402e6a 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -156,7 +156,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok const stream = openZipStream(zipfile, entry); const mode = modeFromEntry(entry); - last = createCancelablePromise(token => throttler.queue(() => stream.then(stream => extractEntry(stream, fileName, mode, targetPath, options, token).then(() => readNextEntry(token)))).then(null!, e)); + last = createCancelablePromise(token => throttler.queue(() => stream.then(stream => extractEntry(stream, fileName, mode, targetPath, options, token).then(() => readNextEntry(token)))).then(null, e)); }); }); } diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index 4ec758b086b..6222be78028 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Menu, MenuItem, BrowserWindow, ipcMain, Event as IpcMainEvent } from 'electron'; +import { Menu, MenuItem, BrowserWindow, ipcMain, IpcMainEvent } from 'electron'; import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions } from 'vs/base/parts/contextmenu/common/contextmenu'; export function registerContextMenuListener(): void { diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 7b60ba4a101..48f75577206 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -8,7 +8,6 @@ import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/li import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; -import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; /** diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts index b310a886f67..bac2223ede6 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -23,7 +23,7 @@ function createScopedOnMessageEvent(senderId: number, eventName: string): Event< export class Server extends IPCServer { - private static Clients = new Map(); + private static readonly Clients = new Map(); private static getOnDidClientConnect(): Event { const onHello = Event.fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index fdd8bcbf06b..9f92529bdac 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -53,7 +53,7 @@ export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass(); export class QuickOpenEntry { private id: string; - private labelHighlights: IHighlight[]; + private labelHighlights?: IHighlight[]; private descriptionHighlights?: IHighlight[]; private detailHighlights?: IHighlight[]; private hidden: boolean | undefined; @@ -160,7 +160,7 @@ export class QuickOpenEntry { /** * Allows to set highlight ranges that should show up for the entry label and optionally description if set. */ - setHighlights(labelHighlights: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void { + setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void { this.labelHighlights = labelHighlights; this.descriptionHighlights = descriptionHighlights; this.detailHighlights = detailHighlights; @@ -169,7 +169,7 @@ export class QuickOpenEntry { /** * Allows to return highlight ranges that should show up for the entry label and description. */ - getHighlights(): [IHighlight[] /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { + getHighlights(): [IHighlight[] | undefined /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { return [this.labelHighlights, this.descriptionHighlights, this.detailHighlights]; } @@ -260,7 +260,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry; } - getHighlights(): [IHighlight[], IHighlight[] | undefined, IHighlight[] | undefined] { + getHighlights(): [IHighlight[] | undefined, IHighlight[] | undefined, IHighlight[] | undefined] { return this.entry ? this.entry.getHighlights() : super.getHighlights(); } @@ -268,7 +268,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.isHidden() : super.isHidden(); } - setHighlights(labelHighlights: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void { + setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void { this.entry ? this.entry.setHighlights(labelHighlights, descriptionHighlights, detailHighlights) : super.setHighlights(labelHighlights, descriptionHighlights, detailHighlights); } @@ -434,7 +434,7 @@ class Renderer implements IRenderer { } } else { DOM.removeClass(groupData.container, 'results-group-separator'); - groupData.container.style.borderTopColor = null; + groupData.container.style.borderTopColor = ''; } // Group Label diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index 68be9fba510..7a3a473d970 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -99,25 +99,40 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { private isDisposed: boolean; private options: IQuickOpenOptions; + // @ts-ignore (legacy widget - to be replaced with quick input) private element: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private tree: ITree; + // @ts-ignore (legacy widget - to be replaced with quick input) private inputBox: InputBox; + // @ts-ignore (legacy widget - to be replaced with quick input) private inputContainer: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private helpText: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private resultCount: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private treeContainer: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private progressBar: ProgressBar; + // @ts-ignore (legacy widget - to be replaced with quick input) private visible: boolean; + // @ts-ignore (legacy widget - to be replaced with quick input) private isLoosingFocus: boolean; private callbacks: IQuickOpenCallbacks; private quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; private container: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private treeElement: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private inputElement: HTMLElement; + // @ts-ignore (legacy widget - to be replaced with quick input) private layoutDimensions: DOM.Dimension; private model: IModel | null; private inputChangingTimeoutHandle: any; + // @ts-ignore (legacy widget - to be replaced with quick input) private styles: IQuickOpenStyles; + // @ts-ignore (legacy widget - to be replaced with quick input) private renderer: Renderer; constructor(container: HTMLElement, callbacks: IQuickOpenCallbacks, options: IQuickOpenOptions) { @@ -381,16 +396,16 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { protected applyStyles(): void { if (this.element) { const foreground = this.styles.foreground ? this.styles.foreground.toString() : null; - const background = this.styles.background ? this.styles.background.toString() : null; - const borderColor = this.styles.borderColor ? this.styles.borderColor.toString() : null; - const widgetShadow = this.styles.widgetShadow ? this.styles.widgetShadow.toString() : null; + const background = this.styles.background ? this.styles.background.toString() : ''; + const borderColor = this.styles.borderColor ? this.styles.borderColor.toString() : ''; + const widgetShadow = this.styles.widgetShadow ? this.styles.widgetShadow.toString() : ''; this.element.style.color = foreground; this.element.style.backgroundColor = background; this.element.style.borderColor = borderColor; - this.element.style.borderWidth = borderColor ? '1px' : null; - this.element.style.borderStyle = borderColor ? 'solid' : null; - this.element.style.boxShadow = widgetShadow ? `0 5px 8px ${widgetShadow}` : null; + this.element.style.borderWidth = borderColor ? '1px' : ''; + this.element.style.borderStyle = borderColor ? 'solid' : ''; + this.element.style.boxShadow = widgetShadow ? `0 5px 8px ${widgetShadow}` : ''; } if (this.progressBar) { diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 85a3ef2d9ac..e73aafe6ad4 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -32,12 +32,12 @@ export interface ISQLiteStorageDatabaseLoggingOptions { export class SQLiteStorageDatabase implements IStorageDatabase { - static IN_MEMORY_PATH = ':memory:'; + static readonly IN_MEMORY_PATH = ':memory:'; get onDidChangeItemsExternal(): Event { return Event.None; } // since we are the only client, there can be no external changes - private static BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY - private static MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement + private static readonly BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY + private static readonly MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement private path: string; private name: string; @@ -82,16 +82,18 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } return this.transaction(connection, () => { + const toInsert = request.insert; + const toDelete = request.delete; // INSERT - if (request.insert && request.insert.size > 0) { + if (toInsert && toInsert.size > 0) { const keysValuesChunks: (string[])[] = []; keysValuesChunks.push([]); // seed with initial empty chunk // Split key/values into chunks of SQLiteStorageDatabase.MAX_HOST_PARAMETERS // so that we can efficiently run the INSERT with as many HOST parameters as possible let currentChunkIndex = 0; - request.insert.forEach((value, key) => { + toInsert.forEach((value, key) => { let keyValueChunk = keysValuesChunks[currentChunkIndex]; if (keyValueChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { @@ -107,7 +109,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { this.prepare(connection, `INSERT INTO ItemTable VALUES ${fill(keysValuesChunk.length / 2, '(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => { const keys: string[] = []; let length = 0; - request.insert!.forEach((value, key) => { + toInsert.forEach((value, key) => { keys.push(key); length += value.length; }); @@ -118,7 +120,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } // DELETE - if (request.delete && request.delete.size) { + if (toDelete && toDelete.size) { const keysChunks: (string[])[] = []; keysChunks.push([]); // seed with initial empty chunk @@ -126,7 +128,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { // so that we can efficiently run the DELETE with as many HOST parameters // as possible let currentChunkIndex = 0; - request.delete.forEach(key => { + toDelete.forEach(key => { let keyChunk = keysChunks[currentChunkIndex]; if (keyChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { @@ -141,7 +143,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { keysChunks.forEach(keysChunk => { this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${fill(keysChunk.length, '?').join(',')})`, stmt => stmt.run(keysChunk), () => { const keys: string[] = []; - request.delete!.forEach(key => { + toDelete.forEach(key => { keys.push(key); }); diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index bdd8f7da8e8..ee6a900f23a 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -398,8 +398,8 @@ function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction | n export class TreeView extends HeightMap { - static BINDING = 'monaco-tree-row'; - static LOADING_DECORATION_DELAY = 800; + static readonly BINDING = 'monaco-tree-row'; + static readonly LOADING_DECORATION_DELAY = 800; private static counter: number = 0; private instance: number; @@ -925,12 +925,11 @@ export class TreeView extends HeightMap { if (!skipDiff) { const lcs = new Diff.LcsDiff( { - getLength: () => previousChildrenIds.length, - getElementAtIndex: (i: number) => previousChildrenIds[i] - }, { - getLength: () => afterModelItems.length, - getElementAtIndex: (i: number) => afterModelItems[i].id - }, + getElements: () => previousChildrenIds + }, + { + getElements: () => afterModelItems.map(item => item.id) + }, null ); diff --git a/src/vs/base/test/common/cancellation.test.ts b/src/vs/base/test/common/cancellation.test.ts index 536b2e21139..88d7d26e6fb 100644 --- a/src/vs/base/test/common/cancellation.test.ts +++ b/src/vs/base/test/common/cancellation.test.ts @@ -95,6 +95,20 @@ suite('CancellationToken', function () { assert.equal(count, 0); }); + test('dispose calls no listeners (unless told to cancel)', function () { + + let count = 0; + + let source = new CancellationTokenSource(); + source.token.onCancellationRequested(function () { + count += 1; + }); + + source.dispose(true); + // source.cancel(); + assert.equal(count, 1); + }); + test('parent cancels child', function () { let parent = new CancellationTokenSource(); diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts index 865c158bab5..47d94c1dbf3 100644 --- a/src/vs/base/test/common/color.test.ts +++ b/src/vs/base/test/common/color.test.ts @@ -196,7 +196,6 @@ suite('Color', () => { test('parseHex', () => { // invalid - assert.deepEqual(Color.Format.CSS.parseHex(null!), null); assert.deepEqual(Color.Format.CSS.parseHex(''), null); assert.deepEqual(Color.Format.CSS.parseHex('#'), null); assert.deepEqual(Color.Format.CSS.parseHex('#0102030'), null); @@ -243,4 +242,4 @@ suite('Color', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/common/diff/diff.test.ts b/src/vs/base/test/common/diff/diff.test.ts index f80a6169f28..be34bca0556 100644 --- a/src/vs/base/test/common/diff/diff.test.ts +++ b/src/vs/base/test/common/diff/diff.test.ts @@ -4,21 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { LcsDiff, IDiffChange, ISequence } from 'vs/base/common/diff/diff'; - -class StringDiffSequence implements ISequence { - - constructor(private source: string) { - } - - getLength() { - return this.source.length; - } - - getElementAtIndex(i: number) { - return this.source.charCodeAt(i); - } -} +import { LcsDiff, IDiffChange, StringDiffSequence } from 'vs/base/common/diff/diff'; function createArray(length: number, value: T): T[] { const r: T[] = []; @@ -123,12 +109,11 @@ suite('Diff - Ported from VS', () => { // doesn't get there first. let predicateCallCount = 0; - let diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { + let diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) { assert.equal(predicateCallCount, 0); predicateCallCount++; - assert.equal(leftSequence.getLength(), left.length); assert.equal(leftIndex, 1); // cancel processing @@ -144,7 +129,7 @@ suite('Diff - Ported from VS', () => { // Cancel after the first match ('c') - diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { + diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) { assert(longestMatchSoFar <= 1); // We never see a match of length > 1 // Continue processing as long as there hasn't been a match made. @@ -157,7 +142,7 @@ suite('Diff - Ported from VS', () => { // Cancel after the second match ('d') - diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { + diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) { assert(longestMatchSoFar <= 2); // We never see a match of length > 2 // Continue processing as long as there hasn't been a match made. @@ -171,7 +156,7 @@ suite('Diff - Ported from VS', () => { // Cancel *one iteration* after the second match ('d') let hitSecondMatch = false; - diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { + diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) { assert(longestMatchSoFar <= 2); // We never see a match of length > 2 let hitYet = hitSecondMatch; @@ -186,7 +171,7 @@ suite('Diff - Ported from VS', () => { // Cancel after the third and final match ('e') - diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { + diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) { assert(longestMatchSoFar <= 3); // We never see a match of length > 3 // Continue processing as long as there hasn't been a match made. diff --git a/src/vs/base/test/common/lazy.test.ts b/src/vs/base/test/common/lazy.test.ts new file mode 100644 index 00000000000..c6a1655513f --- /dev/null +++ b/src/vs/base/test/common/lazy.test.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Lazy } from 'vs/base/common/lazy'; + +suite('Lazy', () => { + + test('lazy values should only be resolved once', () => { + let counter = 0; + const value = new Lazy(() => ++counter); + + assert.strictEqual(value.hasValue(), false); + assert.strictEqual(value.getValue(), 1); + assert.strictEqual(value.hasValue(), true); + assert.strictEqual(value.getValue(), 1); // make sure we did not evaluate again + }); + + test('lazy values handle error case', () => { + let counter = 0; + const value = new Lazy(() => { throw new Error(`${++counter}`); }); + + assert.strictEqual(value.hasValue(), false); + assert.throws(() => value.getValue(), /\b1\b/); + assert.strictEqual(value.hasValue(), true); + assert.throws(() => value.getValue(), /\b1\b/); + }); + + test('map should not cause lazy values to be re-resolved', () => { + let outer = 0; + let inner = 10; + const outerLazy = new Lazy(() => ++outer); + const innerLazy = outerLazy.map(x => [x, ++inner]); + + assert.strictEqual(outerLazy.hasValue(), false); + assert.strictEqual(innerLazy.hasValue(), false); + + assert.deepEqual(innerLazy.getValue(), [1, 11]); + assert.strictEqual(outerLazy.hasValue(), true); + assert.strictEqual(innerLazy.hasValue(), true); + assert.strictEqual(outerLazy.getValue(), 1); + + // make sure we did not evaluate again + assert.strictEqual(outerLazy.getValue(), 1); + assert.deepEqual(innerLazy.getValue(), [1, 11]); + }); + + test('map should should handle error values', () => { + let outer = 0; + let inner = 10; + const outerLazy = new Lazy(() => { throw new Error(`${++outer}`); }); + const innerLazy = outerLazy.map(x => { throw new Error(`${++inner}`); }); + + assert.strictEqual(outerLazy.hasValue(), false); + assert.strictEqual(innerLazy.hasValue(), false); + + assert.throws(() => innerLazy.getValue(), /\b1\b/); // we should get result from outer + assert.strictEqual(outerLazy.hasValue(), true); + assert.strictEqual(innerLazy.hasValue(), true); + assert.throws(() => outerLazy.getValue(), /\b1\b/); + }); +}); diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts new file mode 100644 index 00000000000..69d33de8f17 --- /dev/null +++ b/src/vs/base/test/common/markdownString.test.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { MarkdownString } from 'vs/base/common/htmlContent'; + +suite('markdownString', () => { + + test('escape', () => { + + const mds = new MarkdownString(); + + mds.appendText('# foo\n*bar*'); + + assert.equal(mds.value, '\\# foo\n\n\\*bar\\*'); + }); +}); diff --git a/src/vs/base/test/common/octicon.test.ts b/src/vs/base/test/common/octicon.test.ts index c91055525d2..8ba48579337 100644 --- a/src/vs/base/test/common/octicon.test.ts +++ b/src/vs/base/test/common/octicon.test.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { IMatch } from 'vs/base/common/filters'; -import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon'; +import { matchesFuzzyOcticonAware, parseOcticons, IParsedOcticons } from 'vs/base/common/octicon'; export interface IOcticonFilter { // Returns null if word doesn't match. - (query: string, target: { text: string, octiconOffsets?: number[] }): IMatch[] | null; + (query: string, target: IParsedOcticons): IMatch[] | null; } -function filterOk(filter: IOcticonFilter, word: string, target: { text: string, octiconOffsets?: number[] }, highlights?: { start: number; end: number; }[]) { +function filterOk(filter: IOcticonFilter, word: string, target: IParsedOcticons, highlights?: { start: number; end: number; }[]) { let r = filter(word, target); assert(r); if (highlights) { diff --git a/src/vs/base/test/common/types.test.ts b/src/vs/base/test/common/types.test.ts index 43241b9a9c2..90e174dd000 100644 --- a/src/vs/base/test/common/types.test.ts +++ b/src/vs/base/test/common/types.test.ts @@ -169,6 +169,24 @@ suite('Types', () => { assert(types.isUndefinedOrNull(null)); }); + test('assertIsDefined / assertAreDefined', () => { + assert.throws(() => types.assertIsDefined(undefined)); + assert.throws(() => types.assertIsDefined(null)); + assert.throws(() => types.assertAllDefined(null, undefined)); + assert.throws(() => types.assertAllDefined(true, undefined)); + assert.throws(() => types.assertAllDefined(undefined, false)); + + assert.equal(types.assertIsDefined(true), true); + assert.equal(types.assertIsDefined(false), false); + assert.equal(types.assertIsDefined('Hello'), 'Hello'); + assert.equal(types.assertIsDefined(''), ''); + + const res = types.assertAllDefined(1, true, 'Hello'); + assert.equal(res[0], 1); + assert.equal(res[1], true); + assert.equal(res[2], 'Hello'); + }); + test('validateConstraints', () => { types.validateConstraints([1, 'test', true], [Number, String, Boolean]); types.validateConstraints([1, 'test', true], ['number', 'string', 'boolean']); diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index 910666edf08..b59b7a2b49b 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -14,7 +14,7 @@ default-src 'self'; img-src 'self' https: data: blob:; media-src 'none'; - script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-AMRGFXNZ7mBnD/6F4lTV00XAjE5CBSM7ZeIv3DIp5YM=' 'sha256-meDZW3XhN5JmdjFUrWGhTouRKBiWYtXHltaKnqn/WMo='; + script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-/Ua2yZoIzhImbjP5mnPF6fXTgzsTEtN8hPP1XAB1n6U=' 'sha256-meDZW3XhN5JmdjFUrWGhTouRKBiWYtXHltaKnqn/WMo='; child-src 'self'; frame-src 'self' https://*.vscode-webview-test.com; worker-src 'self'; @@ -50,7 +50,6 @@ 'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, 'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, 'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`, - '@microsoft/applicationinsights-web': `${window.location.origin}/static/remote/web/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`, } }; diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 97d78ce7f9e..f3caa5350bf 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -14,7 +14,7 @@ default-src 'self'; img-src 'self' https: data: blob:; media-src 'none'; - script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-4DqvCTjCHj2KW4QxC/Yt6uBwMRyYiEg7kOoykSEkonQ='; + script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-lrlqPnPED1SjcFocGzCtf6ikbQdTUwJKs8VprSqhvS4='; child-src 'self'; frame-src 'self' https://*.vscode-webview-test.com; worker-src 'self'; @@ -37,7 +37,6 @@ - @@ -55,7 +54,6 @@ 'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, 'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, 'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`, - '@microsoft/applicationinsights-web': `${window.location.origin}/static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`, } }; diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 32b5f1a0903..1f0384a7424 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -10,6 +10,7 @@ import { streamToBuffer } from 'vs/base/common/buffer'; import { Disposable } from 'vs/base/common/lifecycle'; import { request } from 'vs/base/parts/request/browser/request'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; +import { isEqual } from 'vs/base/common/resources'; interface ICredential { service: string; @@ -21,7 +22,7 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider'; - private _credentials!: ICredential[]; + private _credentials: ICredential[] | undefined; private get credentials(): ICredential[] { if (!this._credentials) { try { @@ -102,10 +103,10 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider { - static FETCH_INTERVAL = 500; // fetch every 500ms - static FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min + static readonly FETCH_INTERVAL = 500; // fetch every 500ms + static readonly FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min - static QUERY_KEYS = { + static readonly QUERY_KEYS = { REQUEST_ID: 'vscode-requestId', SCHEME: 'vscode-scheme', AUTHORITY: 'vscode-authority', @@ -203,9 +204,12 @@ class WorkspaceProvider implements IWorkspaceProvider { constructor(public readonly workspace: IWorkspace) { } async open(workspace: IWorkspace, options?: { reuse?: boolean }): Promise { - let targetHref: string | undefined = undefined; + if (options && options.reuse && this.isSame(this.workspace, workspace)) { + return; // return early if workspace is not changing and we are reusing window + } // Empty + let targetHref: string | undefined = undefined; if (!workspace) { targetHref = `${document.location.origin}${document.location.pathname}?ew=true`; } @@ -228,9 +232,31 @@ class WorkspaceProvider implements IWorkspaceProvider { } } } + + private isSame(workspaceA: IWorkspace, workspaceB: IWorkspace): boolean { + if (!workspaceA || !workspaceB) { + return workspaceA === workspaceB; // both empty + } + + if (isFolderToOpen(workspaceA) && isFolderToOpen(workspaceB)) { + return isEqual(workspaceA.folderUri, workspaceB.folderUri); // same workspace + } + + if (isWorkspaceToOpen(workspaceA) && isWorkspaceToOpen(workspaceB)) { + return isEqual(workspaceA.workspaceUri, workspaceB.workspaceUri); // same workspace + } + + return false; + } } -const options: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(document.getElementById('vscode-workbench-web-configuration')!.getAttribute('data-settings')!); +const configElement = document.getElementById('vscode-workbench-web-configuration'); +const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; +if (!configElement || !configElementAttribute) { + throw new Error('Missing web configuration element'); +} + +const options: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute); options.workspaceProvider = new WorkspaceProvider(options.folderUri ? { folderUri: URI.revive(options.folderUri) } : options.workspaceUri ? { workspaceUri: URI.revive(options.workspaceUri) } : undefined); options.urlCallbackProvider = new PollingURLCallbackProvider(); options.credentialsProvider = new LocalStorageCredentialsProvider(); diff --git a/src/vs/code/electron-browser/issue/issueReporter.js b/src/vs/code/electron-browser/issue/issueReporter.js index 71c2b34a4e5..5d11dd15ae4 100644 --- a/src/vs/code/electron-browser/issue/issueReporter.js +++ b/src/vs/code/electron-browser/issue/issueReporter.js @@ -10,4 +10,4 @@ const bootstrapWindow = require('../../../../bootstrap-window'); bootstrapWindow.load(['vs/code/electron-browser/issue/issueReporterMain'], function (issueReporter, configuration) { issueReporter.startup(configuration); -}, { forceEnableDeveloperKeybindings: true }); \ No newline at end of file +}, { forceEnableDeveloperKeybindings: true, disallowReloadKeybinding: true }); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index c65686451de..3191cdc737a 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -37,7 +37,6 @@ import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; import { Button } from 'vs/base/browser/ui/button/button'; -import { withUndefinedAsNull } from 'vs/base/common/types'; import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; @@ -232,7 +231,7 @@ export class IssueReporter extends Disposable { styleTag.innerHTML = content.join('\n'); document.head.appendChild(styleTag); - document.body.style.color = withUndefinedAsNull(styles.color); + document.body.style.color = styles.color || ''; } private handleExtensionData(extensions: IssueReporterExtensionData[]) { diff --git a/src/vs/code/electron-browser/processExplorer/media/collapsed.svg b/src/vs/code/electron-browser/processExplorer/media/collapsed.svg old mode 100755 new mode 100644 diff --git a/src/vs/code/electron-browser/processExplorer/media/expanded.svg b/src/vs/code/electron-browser/processExplorer/media/expanded.svg old mode 100755 new mode 100644 diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index d3a0e9dab87..a3899b854fb 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -13,7 +13,7 @@ import { IBackupWorkspacesFormat } from 'vs/platform/backup/node/backup'; export class StorageDataCleaner extends Disposable { // Workspace/Folder storage names are MD5 hashes (128bits / 4 due to hex presentation) - private static NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4; + private static readonly NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4; constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index dd200cde511..ebb99a4b34c 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, Event as IpcMainEvent, BrowserWindow } from 'electron'; +import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, IpcMainEvent, BrowserWindow } from 'electron'; import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; -import { WindowsManager } from 'vs/code/electron-main/windows'; +import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService'; import { OpenContext, IWindowOpenable } from 'vs/platform/windows/common/windows'; import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; @@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IStateService } from 'vs/platform/state/node/state'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IURLService } from 'vs/platform/url/common/url'; +import { IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -74,8 +74,7 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; -import { IElectronService } from 'vs/platform/electron/node/electron'; -import { ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; +import { IElectronMainService, ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; import { assign } from 'vs/base/common/objects'; import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; @@ -377,7 +376,7 @@ export class CodeApplication extends Disposable { // Create driver if (this.environmentService.driverHandle) { - const server = await serveDriver(electronIpcServer, this.environmentService.driverHandle!, this.environmentService, appInstantiationService); + const server = await serveDriver(electronIpcServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService); this.logService.info('Driver started at:', this.environmentService.driverHandle); this._register(server); @@ -450,7 +449,7 @@ export class CodeApplication extends Disposable { break; } - services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv])); + services.set(IWindowsMainService, new SyncDescriptor(WindowsMainService, [machineId, this.userEnv])); services.set(IDialogMainService, new SyncDescriptor(DialogMainService)); services.set(ISharedProcessMainService, new SyncDescriptor(SharedProcessMainService, [sharedProcess])); services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService)); @@ -459,7 +458,7 @@ export class CodeApplication extends Disposable { services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel])); services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); - services.set(IElectronService, new SyncDescriptor(ElectronMainService)); + services.set(IElectronMainService, new SyncDescriptor(ElectronMainService)); services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService)); services.set(IMenubarService, new SyncDescriptor(MenubarMainService)); @@ -546,8 +545,8 @@ export class CodeApplication extends Disposable { const issueChannel = createChannelReceiver(issueService); electronIpcServer.registerChannel('issue', issueChannel); - const electronService = accessor.get(IElectronService); - const electronChannel = createChannelReceiver(electronService); + const electronMainService = accessor.get(IElectronMainService); + const electronChannel = createChannelReceiver(electronMainService); electronIpcServer.registerChannel('electron', electronChannel); sharedProcessClient.then(client => client.registerChannel('electron', electronChannel)); @@ -588,12 +587,14 @@ export class CodeApplication extends Disposable { // Create a URL handler to open file URIs in the active window const environmentService = accessor.get(IEnvironmentService); urlService.registerHandler({ - async handleURL(uri: URI): Promise { + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { // Catch file URLs if (uri.authority === Schemas.file && !!uri.path) { const cli = assign(Object.create(null), environmentService.args); - const urisToOpen = [{ fileUri: uri }]; + + // hey Ben, we need to convert this `code://file` URI into a `file://` URI + const urisToOpen = [{ fileUri: URI.file(uri.fsPath) }]; windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: true }); @@ -605,7 +606,7 @@ export class CodeApplication extends Disposable { }); // Create a URL handler which forwards to the last active window - const activeWindowManager = new ActiveWindowManager(electronService); + const activeWindowManager = new ActiveWindowManager(electronMainService); const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter); const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', urlHandlerRouter); @@ -615,7 +616,7 @@ export class CodeApplication extends Disposable { // if there is none if (isMacintosh) { urlService.registerHandler({ - async handleURL(uri: URI): Promise { + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { if (windowsMainService.getWindowCount() === 0) { const cli = { ...environmentService.args }; const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true, gotoLineMode: true }); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 1efa3b6e3bf..9a4f97020eb 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -6,7 +6,7 @@ import 'vs/platform/update/common/update.config.contribution'; import { app, dialog } from 'electron'; import { assign } from 'vs/base/common/objects'; -import * as platform from 'vs/base/common/platform'; +import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; import product from 'vs/platform/product/common/product'; import { parseMainProcessArgv } from 'vs/platform/environment/node/argvHelper'; import { addArg, createWaitMarkerFile } from 'vs/platform/environment/node/argv'; @@ -132,7 +132,7 @@ class CodeMain { } } - private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, typeof process.env] { + private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment] { const services = new ServiceCollection(); const environmentService = new EnvironmentService(args, process.execPath); @@ -174,16 +174,17 @@ class CodeMain { return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); } - private patchEnvironment(environmentService: IEnvironmentService): typeof process.env { - const instanceEnvironment: typeof process.env = { - VSCODE_IPC_HOOK: environmentService.mainIPCHandle, - VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'], - VSCODE_LOGS: process.env['VSCODE_LOGS'] + private patchEnvironment(environmentService: IEnvironmentService): IProcessEnvironment { + const instanceEnvironment: IProcessEnvironment = { + VSCODE_IPC_HOOK: environmentService.mainIPCHandle }; - if (process.env['VSCODE_PORTABLE']) { - instanceEnvironment['VSCODE_PORTABLE'] = process.env['VSCODE_PORTABLE']; - } + ['VSCODE_NLS_CONFIG', 'VSCODE_LOGS', 'VSCODE_PORTABLE'].forEach(key => { + const value = process.env[key]; + if (typeof value === 'string') { + instanceEnvironment[key] = value; + } + }); assign(process.env, instanceEnvironment); @@ -213,7 +214,7 @@ class CodeMain { } // Since we are the second instance, we do not want to show the dock - if (platform.isMacintosh) { + if (isMacintosh) { app.dock.hide(); } @@ -224,7 +225,7 @@ class CodeMain { } catch (error) { // Handle unexpected connection errors by showing a dialog to the user - if (!retry || platform.isWindows || error.code !== 'ECONNREFUSED') { + if (!retry || isWindows || error.code !== 'ECONNREFUSED') { if (error.code === 'EPERM') { this.showStartupWarningDialog( localize('secondInstanceAdmin', "A second instance of {0} is already running as administrator.", product.nameShort), @@ -290,16 +291,16 @@ class CodeMain { } // Windows: allow to set foreground - if (platform.isWindows) { + if (isWindows) { await this.windowsAllowSetForegroundWindow(launchService, logService); } // Send environment over... logService.trace('Sending env to running instance...'); - await launchService.start(environmentService.args, process.env as platform.IProcessEnvironment); + await launchService.start(environmentService.args, process.env as IProcessEnvironment); // Cleanup - await client.dispose(); + client.dispose(); // Now that we started, make sure the warning dialog is prevented if (startupWarningDialogHandle) { @@ -317,7 +318,7 @@ class CodeMain { } // dock might be hidden at this case due to a retry - if (platform.isMacintosh) { + if (isMacintosh) { app.dock.show(); } @@ -359,7 +360,7 @@ class CodeMain { } private async windowsAllowSetForegroundWindow(launchService: ILaunchMainService, logService: ILogService): Promise { - if (platform.isWindows) { + if (isWindows) { const processId = await launchService.getMainProcessId(); logService.trace('Sending some foreground love to the running instance:', processId); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index f136d7c7232..3a216fca8c0 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -6,6 +6,7 @@ import * as path from 'vs/base/common/path'; import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; +import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment } from 'electron'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; @@ -27,6 +28,9 @@ import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainServ import { endsWith } from 'vs/base/common/strings'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IFileService } from 'vs/platform/files/common/files'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; const RUN_TEXTMATE_IN_WORKER = false; @@ -48,6 +52,11 @@ interface ITouchBarSegment extends SegmentedControlSegment { id: string; } +const enum WindowError { + UNRESPONSIVE = 1, + CRASHED = 2 +} + export class CodeWindow extends Disposable implements ICodeWindow { private static readonly MIN_WIDTH = 200; @@ -55,19 +64,25 @@ export class CodeWindow extends Disposable implements ICodeWindow { private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 - private hiddenTitleBarStyle: boolean; - private showTimeoutHandle: NodeJS.Timeout; - private _id: number; - private _win: BrowserWindow; + private readonly _onClose = this._register(new Emitter()); + readonly onClose: CommonEvent = this._onClose.event; + + private readonly _onDestroy = this._register(new Emitter()); + readonly onDestroy: CommonEvent = this._onDestroy.event; + + private readonly _onLoad = this._register(new Emitter()); + readonly onLoad: CommonEvent = this._onLoad.event; + + private hiddenTitleBarStyle: boolean | undefined; + private showTimeoutHandle: NodeJS.Timeout | undefined; private _lastFocusTime: number; private _readyState: ReadyState; private windowState: IWindowState; - private currentMenuBarVisibility: MenuBarVisibility; - private representedFilename: string; + private currentMenuBarVisibility: MenuBarVisibility | undefined; + private representedFilename: string | undefined; private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[]; - private currentConfig: IWindowConfiguration; private pendingLoadConfig?: IWindowConfiguration; private marketplaceHeadersPromise: Promise; @@ -83,6 +98,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { @IThemeMainService private readonly themeMainService: IThemeMainService, @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IDialogMainService private readonly dialogMainService: IDialogMainService ) { super(); @@ -91,8 +108,111 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._readyState = ReadyState.NONE; this.whenReadyCallbacks = []; - // create browser window - this.createBrowserWindow(config); + //#region create browser window + { + // Load window state + const [state, hasMultipleDisplays] = this.restoreWindowState(config.state); + this.windowState = state; + + // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) + const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); + + const options: BrowserWindowConstructorOptions = { + width: this.windowState.width, + height: this.windowState.height, + x: this.windowState.x, + y: this.windowState.y, + backgroundColor: this.themeMainService.getBackgroundColor(), + minWidth: CodeWindow.MIN_WIDTH, + minHeight: CodeWindow.MIN_HEIGHT, + show: !isFullscreenOrMaximized, + title: product.nameLong, + webPreferences: { + // By default if Code is in the background, intervals and timeouts get throttled, so we + // want to enforce that Code stays in the foreground. This triggers a disable_hidden_ + // flag that Electron provides via patch: + // https://github.com/electron/libchromiumcontent/blob/master/patches/common/chromium/disable_hidden.patch + backgroundThrottling: false, + nodeIntegration: true, + nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER, + webviewTag: true + } + }; + + if (isLinux) { + options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s) + } + + const windowConfig = this.configurationService.getValue('window'); + + if (isMacintosh && !this.useNativeFullScreen()) { + options.fullscreenable = false; // enables simple fullscreen mode + } + + if (isMacintosh) { + options.acceptFirstMouse = true; // enabled by default + + if (windowConfig && windowConfig.clickThroughInactive === false) { + options.acceptFirstMouse = false; + } + } + + const useNativeTabs = isMacintosh && windowConfig && windowConfig.nativeTabs === true; + if (useNativeTabs) { + options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs + } + + const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService, !!config.extensionDevelopmentPath) === 'custom'; + if (useCustomTitleStyle) { + options.titleBarStyle = 'hidden'; + this.hiddenTitleBarStyle = true; + if (!isMacintosh) { + options.frame = false; + } + } + + // Create the browser window. + this._win = new BrowserWindow(options); + this._id = this._win.id; + + if (isMacintosh && useCustomTitleStyle) { + this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any + } + + // TODO@Ben (Electron 4 regression): when running on multiple displays where the target display + // to open the window has a larger resolution than the primary display, the window will not size + // correctly unless we set the bounds again (https://github.com/microsoft/vscode/issues/74872) + // + // However, when running with native tabs with multiple windows we cannot use this workaround + // because there is a potential that the new window will be added as native tab instead of being + // a window on its own. In that case calling setBounds() would cause https://github.com/microsoft/vscode/issues/75830 + if (isMacintosh && hasMultipleDisplays && (!useNativeTabs || BrowserWindow.getAllWindows().length === 1)) { + if ([this.windowState.width, this.windowState.height, this.windowState.x, this.windowState.y].every(value => typeof value === 'number')) { + const ensuredWindowState = this.windowState as Required; + this._win.setBounds({ + width: ensuredWindowState.width, + height: ensuredWindowState.height, + x: ensuredWindowState.x, + y: ensuredWindowState.y + }); + } + } + + if (isFullscreenOrMaximized) { + this._win.maximize(); + + if (this.windowState.mode === WindowMode.Fullscreen) { + this.setFullScreen(true); + } + + if (!this._win.isVisible()) { + this._win.show(); // to reduce flicker from the default window size to maximize, we only show after maximize + } + } + + this._lastFocusTime = Date.now(); // since we show directly, we need to set the last focus time too + } + //#endregion // respect configured menu bar visibility this.onConfigurationUpdated(); @@ -101,139 +221,26 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.createTouchBar(); // Request handling - this.handleMarketplaceRequests(); + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService); // Eventing this.registerListeners(); } - private createBrowserWindow(config: IWindowCreationOptions): void { + private currentConfig: IWindowConfiguration | undefined; + get config(): IWindowConfiguration | undefined { return this.currentConfig; } - // Load window state - const [state, hasMultipleDisplays] = this.restoreWindowState(config.state); - this.windowState = state; + private _id: number; + get id(): number { return this._id; } - // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) - const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); + private _win: BrowserWindow; + get win(): BrowserWindow { return this._win; } - const options: BrowserWindowConstructorOptions = { - width: this.windowState.width, - height: this.windowState.height, - x: this.windowState.x, - y: this.windowState.y, - backgroundColor: this.themeMainService.getBackgroundColor(), - minWidth: CodeWindow.MIN_WIDTH, - minHeight: CodeWindow.MIN_HEIGHT, - show: !isFullscreenOrMaximized, - title: product.nameLong, - webPreferences: { - // By default if Code is in the background, intervals and timeouts get throttled, so we - // want to enforce that Code stays in the foreground. This triggers a disable_hidden_ - // flag that Electron provides via patch: - // https://github.com/electron/libchromiumcontent/blob/master/patches/common/chromium/disable_hidden.patch - backgroundThrottling: false, - nodeIntegration: true, - nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER, - webviewTag: true - } - }; + get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } - if (isLinux) { - options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s) - } + get isExtensionDevelopmentHost(): boolean { return !!(this.config && this.config.extensionDevelopmentPath); } - const windowConfig = this.configurationService.getValue('window'); - - if (isMacintosh && !this.useNativeFullScreen()) { - options.fullscreenable = false; // enables simple fullscreen mode - } - - if (isMacintosh) { - options.acceptFirstMouse = true; // enabled by default - - if (windowConfig && windowConfig.clickThroughInactive === false) { - options.acceptFirstMouse = false; - } - } - - const useNativeTabs = isMacintosh && windowConfig && windowConfig.nativeTabs === true; - if (useNativeTabs) { - options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs - } - - const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService, !!config.extensionDevelopmentPath) === 'custom'; - if (useCustomTitleStyle) { - options.titleBarStyle = 'hidden'; - this.hiddenTitleBarStyle = true; - if (!isMacintosh) { - options.frame = false; - } - } - - // Create the browser window. - this._win = new BrowserWindow(options); - this._id = this._win.id; - - if (isMacintosh && useCustomTitleStyle) { - this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any - } - - // TODO@Ben (Electron 4 regression): when running on multiple displays where the target display - // to open the window has a larger resolution than the primary display, the window will not size - // correctly unless we set the bounds again (https://github.com/microsoft/vscode/issues/74872) - // - // However, when running with native tabs with multiple windows we cannot use this workaround - // because there is a potential that the new window will be added as native tab instead of being - // a window on its own. In that case calling setBounds() would cause https://github.com/microsoft/vscode/issues/75830 - if (isMacintosh && hasMultipleDisplays && (!useNativeTabs || BrowserWindow.getAllWindows().length === 1)) { - if ([this.windowState.width, this.windowState.height, this.windowState.x, this.windowState.y].every(value => typeof value === 'number')) { - this._win.setBounds({ - width: this.windowState.width!, - height: this.windowState.height!, - x: this.windowState.x!, - y: this.windowState.y! - }); - } - } - - if (isFullscreenOrMaximized) { - this._win.maximize(); - - if (this.windowState.mode === WindowMode.Fullscreen) { - this.setFullScreen(true); - } - - if (!this._win.isVisible()) { - this._win.show(); // to reduce flicker from the default window size to maximize, we only show after maximize - } - } - - this._lastFocusTime = Date.now(); // since we show directly, we need to set the last focus time too - } - - hasHiddenTitleBarStyle(): boolean { - return this.hiddenTitleBarStyle; - } - - get isExtensionDevelopmentHost(): boolean { - return !!this.config.extensionDevelopmentPath; - } - - get isExtensionTestHost(): boolean { - return !!this.config.extensionTestsPath; - } - - get config(): IWindowConfiguration { - return this.currentConfig; - } - - get id(): number { - return this._id; - } - - get win(): BrowserWindow { - return this._win; - } + get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); } setRepresentedFilename(filename: string): void { if (isMacintosh) { @@ -243,7 +250,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - getRepresentedFilename(): string { + getRepresentedFilename(): string | undefined { if (isMacintosh) { return this.win.getRepresentedFilename(); } @@ -263,25 +270,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win.focus(); } - get lastFocusTime(): number { - return this._lastFocusTime; - } + get lastFocusTime(): number { return this._lastFocusTime; } - get backupPath(): string | undefined { - return this.currentConfig ? this.currentConfig.backupPath : undefined; - } + get backupPath(): string | undefined { return this.currentConfig ? this.currentConfig.backupPath : undefined; } - get openedWorkspace(): IWorkspaceIdentifier | undefined { - return this.currentConfig ? this.currentConfig.workspace : undefined; - } + get openedWorkspace(): IWorkspaceIdentifier | undefined { return this.currentConfig ? this.currentConfig.workspace : undefined; } - get openedFolderUri(): URI | undefined { - return this.currentConfig ? this.currentConfig.folderUri : undefined; - } + get openedFolderUri(): URI | undefined { return this.currentConfig ? this.currentConfig.folderUri : undefined; } - get remoteAuthority(): string | undefined { - return this.currentConfig ? this.currentConfig.remoteAuthority : undefined; - } + get remoteAuthority(): string | undefined { return this.currentConfig ? this.currentConfig.remoteAuthority : undefined; } setReady(): void { this._readyState = ReadyState.READY; @@ -307,26 +304,34 @@ export class CodeWindow extends Disposable implements ICodeWindow { return this._readyState === ReadyState.READY; } - private handleMarketplaceRequests(): void { + get whenClosedOrLoaded(): Promise { + return new Promise(resolve => { - // Resolve marketplace headers - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService); + function handle() { + closeListener.dispose(); + loadListener.dispose(); - // Inject headers when requests are incoming - const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; - this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => { - this.marketplaceHeadersPromise.then(headers => { - const requestHeaders = objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined }; - if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) { - requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`; - } - cb({ cancel: false, requestHeaders }); - }); + resolve(); + } + + const closeListener = this.onClose(() => handle()); + const loadListener = this.onLoad(() => handle()); }); } private registerListeners(): void { + // Crashes & Unrsponsive + this._win.webContents.on('crashed', () => this.onWindowError(WindowError.CRASHED)); + this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE)); + + // Window close + this._win.on('closed', () => { + this._onClose.fire(); + + this.dispose(); + }); + // Prevent loading of svgs this._win.webContents.session.webRequest.onBeforeRequest(null!, (details, callback) => { if (details.url.indexOf('.svg') > 0) { @@ -376,7 +381,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { return; // disposed } - if (!this.useNativeFullScreen() && this.isFullScreen()) { + if (!this.useNativeFullScreen() && this.isFullScreen) { this.setFullScreen(false); this.setFullScreen(true); } @@ -430,13 +435,97 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Handle Workspace events this._register(this.workspacesMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); + + // Inject headers when requests are incoming + const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; + this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => { + this.marketplaceHeadersPromise.then(headers => { + const requestHeaders = objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined }; + if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) { + requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`; + } + cb({ cancel: false, requestHeaders }); + }); + }); + } + + private onWindowError(error: WindowError): void { + this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); + + type WindowErrorClassification = { + type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + }; + type WindowErrorEvent = { + type: WindowError; + }; + this.telemetryService.publicLog2('windowerror', { type: error }); + + // Unresponsive + if (error === WindowError.UNRESPONSIVE) { + if (this.isExtensionDevelopmentHost || this.isExtensionTestHost || (this._win && this._win.webContents && this._win.webContents.isDevToolsOpened())) { + // TODO@Ben Workaround for https://github.com/Microsoft/vscode/issues/56994 + // In certain cases the window can report unresponsiveness because a breakpoint was hit + // and the process is stopped executing. The most typical cases are: + // - devtools are opened and debugging happens + // - window is an extensions development host that is being debugged + // - window is an extension test development host that is being debugged + return; + } + + // Show Dialog + this.dialogMainService.showMessageBox({ + title: product.nameLong, + type: 'warning', + buttons: [mnemonicButtonLabel(nls.localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(nls.localize({ key: 'wait', comment: ['&& denotes a mnemonic'] }, "&&Keep Waiting")), mnemonicButtonLabel(nls.localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], + message: nls.localize('appStalled', "The window is no longer responding"), + detail: nls.localize('appStalledDetail', "You can reopen or close the window or keep waiting."), + noLink: true + }, this._win).then(result => { + if (!this._win) { + return; // Return early if the window has been going down already + } + + if (result.response === 0) { + this.reload(); + } else if (result.response === 2) { + this.destroyWindow(); + } + }); + } + + // Crashed + else { + this.dialogMainService.showMessageBox({ + title: product.nameLong, + type: 'warning', + buttons: [mnemonicButtonLabel(nls.localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(nls.localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], + message: nls.localize('appCrashed', "The window has crashed"), + detail: nls.localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), + noLink: true + }, this._win).then(result => { + if (!this._win) { + return; // Return early if the window has been going down already + } + + if (result.response === 0) { + this.reload(); + } else if (result.response === 1) { + this.destroyWindow(); + } + }); + } + } + + private destroyWindow(): void { + this._onDestroy.fire(); // 'close' event will not be fired on destroy(), so signal crash via explicit event + this._win.destroy(); // make sure to destroy the window as it has crashed } private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void { // Make sure to update our workspace config if we detect that it // was deleted - if (this.openedWorkspace && this.openedWorkspace.id === workspace.id) { + if (this.openedWorkspace && this.openedWorkspace.id === workspace.id && this.currentConfig) { this.currentConfig.workspace = undefined; } } @@ -510,6 +599,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { } }, 10000); } + + // Event + this._onLoad.fire(); } reload(configurationIn?: IWindowConfiguration, cli?: ParsedArgs): void { @@ -553,7 +645,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Set fullscreen state - windowConfiguration.fullscreen = this.isFullScreen(); + windowConfiguration.fullscreen = this.isFullScreen; // Set Accessibility Config let autoDetectHighContrast = true; @@ -561,11 +653,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { autoDetectHighContrast = false; } windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme(); - windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled(); + windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related windowConfiguration.maximized = this._win.isMaximized(); - windowConfiguration.frameless = this.hasHiddenTitleBarStyle() && !isMacintosh; + windowConfiguration.frameless = this.hasHiddenTitleBarStyle && !isMacintosh; // Dump Perf Counters windowConfiguration.perfEntries = perf.exportEntries(); @@ -611,7 +703,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // fullscreen gets special treatment - if (this.isFullScreen()) { + if (this.isFullScreen) { const display = screen.getDisplayMatching(this.getBounds()); const defaultState = defaultWindowState(); @@ -786,7 +878,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } toggleFullScreen(): void { - this.setFullScreen(!this.isFullScreen()); + this.setFullScreen(!this.isFullScreen); } private setFullScreen(fullscreen: boolean): void { @@ -802,12 +894,12 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen'); // Respect configured menu bar visibility or default to toggle if not set - this.setMenuBarVisibility(this.currentMenuBarVisibility, false); + if (this.currentMenuBarVisibility) { + this.setMenuBarVisibility(this.currentMenuBarVisibility, false); + } } - isFullScreen(): boolean { - return this._win.isFullScreen() || this._win.isSimpleFullScreen(); - } + get isFullScreen(): boolean { return this._win.isFullScreen() || this._win.isSimpleFullScreen(); } private setNativeFullScreen(fullscreen: boolean): void { if (this._win.isSimpleFullScreen()) { @@ -883,7 +975,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private doSetMenuBarVisibility(visibility: MenuBarVisibility): void { - const isFullscreen = this.isFullScreen(); + const isFullscreen = this.isFullScreen; switch (visibility) { case ('default'): diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index ef61a9e41b2..9e814c1b58b 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -14,7 +14,7 @@ import * as paths from 'vs/base/common/path'; import { whenDeleted, writeFileSync } from 'vs/base/node/pfs'; import { findFreePort, randomPort } from 'vs/base/node/ports'; import { resolveTerminalEncoding } from 'vs/base/node/encoding'; -import { isWindows } from 'vs/base/common/platform'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { ProfilingSession, Target } from 'v8-inspect-profiler'; import { isString } from 'vs/base/common/types'; @@ -360,6 +360,10 @@ export async function main(argv: string[]): Promise { options['stdio'] = 'ignore'; } + if (isLinux) { + addArg(argv, '--no-sandbox'); // Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox + } + const child = spawn(process.execPath, argv.slice(2), options); if (args.wait && waitMarkerFilePath) { diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts index ce4e1b0227a..fb191e7fe08 100644 --- a/src/vs/code/node/paths.ts +++ b/src/vs/code/node/paths.ts @@ -19,12 +19,14 @@ export function validatePaths(args: ParsedArgs): ParsedArgs { args._ = []; } - // Normalize paths and watch out for goto line mode - const paths = doValidatePaths(args._, args.goto); + if (!args['remote']) { + // Normalize paths and watch out for goto line mode + const paths = doValidatePaths(args._, args.goto); + args._ = paths; + } // Update environment - args._ = paths; - args.diff = args.diff && paths.length === 2; + args.diff = args.diff && args._.length === 2; return args; } @@ -135,4 +137,4 @@ function toPath(p: IPathWithLineAndColumn): string { } return segments.join(':'); -} \ No newline at end of file +} diff --git a/src/vs/code/test/electron-main/windowsStateStorage.test.ts b/src/vs/code/test/electron-main/windowsStateStorage.test.ts index d87d855a36b..6094456c6f5 100644 --- a/src/vs/code/test/electron-main/windowsStateStorage.test.ts +++ b/src/vs/code/test/electron-main/windowsStateStorage.test.ts @@ -6,11 +6,11 @@ import * as assert from 'assert'; import * as os from 'os'; import * as path from 'vs/base/common/path'; -import { restoreWindowsState, getWindowsStateStoreData } from 'vs/code/electron-main/windowsStateStorage'; +import { restoreWindowsState, getWindowsStateStoreData } from 'vs/platform/windows/electron-main/windowsStateStorage'; import { IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; -import { IWindowsState, IWindowState } from 'vs/code/electron-main/windows'; +import { IWindowsState, IWindowState } from 'vs/platform/windows/electron-main/windowsMainService'; function getUIState(): IWindowUIState { return { @@ -288,4 +288,4 @@ suite('Windows State Storing', () => { }); -}); \ No newline at end of file +}); diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index 601a8ae8ad7..21a54663412 100644 --- a/src/vs/editor/browser/config/charWidthReader.ts +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -71,6 +71,7 @@ class DomCharWidthReader { regularDomNode.style.fontFamily = this._bareFontInfo.getMassagedFontFamily(); regularDomNode.style.fontWeight = this._bareFontInfo.fontWeight; regularDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; + regularDomNode.style.fontFeatureSettings = this._bareFontInfo.fontFeatureSettings; regularDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; regularDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; container.appendChild(regularDomNode); @@ -79,6 +80,7 @@ class DomCharWidthReader { boldDomNode.style.fontFamily = this._bareFontInfo.getMassagedFontFamily(); boldDomNode.style.fontWeight = 'bold'; boldDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; + boldDomNode.style.fontFeatureSettings = this._bareFontInfo.fontFeatureSettings; boldDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; boldDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; container.appendChild(boldDomNode); @@ -87,6 +89,7 @@ class DomCharWidthReader { italicDomNode.style.fontFamily = this._bareFontInfo.getMassagedFontFamily(); italicDomNode.style.fontWeight = this._bareFontInfo.fontWeight; italicDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; + italicDomNode.style.fontFeatureSettings = this._bareFontInfo.fontFeatureSettings; italicDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; italicDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; italicDomNode.style.fontStyle = 'italic'; diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 6f28ca355ff..471c97e1162 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -11,7 +11,7 @@ import * as platform from 'vs/base/common/platform'; import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { EditorOption, IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions'; +import { EditorOption, IEditorConstructionOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { IDimension } from 'vs/editor/common/editorCommon'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; @@ -79,6 +79,7 @@ export interface ISerializedFontInfo { readonly fontFamily: string; readonly fontWeight: string; readonly fontSize: number; + fontFeatureSettings: string; readonly lineHeight: number; readonly letterSpacing: number; readonly isMonospace: boolean; @@ -151,11 +152,14 @@ class CSSBasedConfiguration extends Disposable { return this._cache.getValues().filter(item => item.isTrusted); } - public restoreFontInfo(savedFontInfo: ISerializedFontInfo[]): void { + public restoreFontInfo(savedFontInfos: ISerializedFontInfo[]): void { // Take all the saved font info and insert them in the cache without the trusted flag. // The reason for this is that a font might have been installed on the OS in the meantime. - for (let i = 0, len = savedFontInfo.length; i < len; i++) { - const fontInfo = new FontInfo(savedFontInfo[i], false); + for (let i = 0, len = savedFontInfos.length; i < len; i++) { + const savedFontInfo = savedFontInfos[i]; + // compatibility with older versions of VS Code which did not store this... + savedFontInfo.fontFeatureSettings = savedFontInfo.fontFeatureSettings || EditorFontLigatures.OFF; + const fontInfo = new FontInfo(savedFontInfo, false); this._writeToCache(fontInfo, fontInfo); } } @@ -171,6 +175,7 @@ class CSSBasedConfiguration extends Disposable { fontFamily: readConfig.fontFamily, fontWeight: readConfig.fontWeight, fontSize: readConfig.fontSize, + fontFeatureSettings: readConfig.fontFeatureSettings, lineHeight: readConfig.lineHeight, letterSpacing: readConfig.letterSpacing, isMonospace: readConfig.isMonospace, @@ -249,9 +254,9 @@ class CSSBasedConfiguration extends Disposable { const maxDigitWidth = Math.max(digit0.width, digit1.width, digit2.width, digit3.width, digit4.width, digit5.width, digit6.width, digit7.width, digit8.width, digit9.width); - let isMonospace = true; + let isMonospace = (bareFontInfo.fontFeatureSettings === EditorFontLigatures.OFF); const referenceWidth = monospace[0].width; - for (let i = 1, len = monospace.length; i < len; i++) { + for (let i = 1, len = monospace.length; isMonospace && i < len; i++) { const diff = referenceWidth - monospace[i].width; if (diff < -0.001 || diff > 0.001) { isMonospace = false; @@ -276,6 +281,7 @@ class CSSBasedConfiguration extends Disposable { fontFamily: bareFontInfo.fontFamily, fontWeight: bareFontInfo.fontWeight, fontSize: bareFontInfo.fontSize, + fontFeatureSettings: bareFontInfo.fontFeatureSettings, lineHeight: bareFontInfo.lineHeight, letterSpacing: bareFontInfo.letterSpacing, isMonospace: isMonospace, @@ -294,6 +300,7 @@ export class Configuration extends CommonEditorConfiguration { domNode.style.fontFamily = fontInfo.getMassagedFontFamily(); domNode.style.fontWeight = fontInfo.fontWeight; domNode.style.fontSize = fontInfo.fontSize + 'px'; + domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings; domNode.style.lineHeight = fontInfo.lineHeight + 'px'; domNode.style.letterSpacing = fontInfo.letterSpacing + 'px'; } @@ -302,6 +309,7 @@ export class Configuration extends CommonEditorConfiguration { domNode.setFontFamily(fontInfo.getMassagedFontFamily()); domNode.setFontWeight(fontInfo.fontWeight); domNode.setFontSize(fontInfo.fontSize); + domNode.setFontFeatureSettings(fontInfo.fontFeatureSettings); domNode.setLineHeight(fontInfo.lineHeight); domNode.setLetterSpacing(fontInfo.letterSpacing); } diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index dd8979daabc..357f1f542be 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -65,7 +65,7 @@ export interface IPointerHandlerHelper { export class MouseHandler extends ViewEventHandler { - static MOUSE_MOVE_MINIMUM_TIME = 100; // ms + static readonly MOUSE_MOVE_MINIMUM_TIME = 100; // ms protected _context: ViewContext; protected viewController: ViewController; diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index c025c958a0f..c396ab9b09d 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -972,7 +972,7 @@ export class MouseTargetFactory { // Thank you browsers for making this so 'easy' :) - if (document.caretRangeFromPoint) { + if (typeof document.caretRangeFromPoint === 'function') { return this._doHitTestWithCaretRangeFromPoint(ctx, request); diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 7d5cab28c98..2a734c4adc8 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -66,7 +66,7 @@ interface LocalClipboardMetadata { * we can fetch the previous metadata. */ class LocalClipboardMetadataManager { - public static INSTANCE = new LocalClipboardMetadataManager(); + public static readonly INSTANCE = new LocalClipboardMetadataManager(); private _lastState: LocalClipboardMetadata | null; diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index c467768414b..ffccc8832bd 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -16,17 +16,17 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IConstructorSignature1, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { withNullAsUndefined } from 'vs/base/common/types'; -export type ServicesAccessor = ServicesAccessor; +export type ServicesAccessor = InstantiationServicesAccessor; export type IEditorContributionCtor = IConstructorSignature1; export type EditorTelemetryDataFragment = { - target: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - snippet: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + target: { classification: 'SystemMetaData', purpose: 'FeatureInsight', }; + snippet: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true, }; }; //#region Command @@ -224,8 +224,8 @@ export abstract class EditorAction extends EditorCommand { protected reportTelemetry(accessor: ServicesAccessor, editor: ICodeEditor) { type EditorActionInvokedClassification = { - name: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + name: { classification: 'SystemMetaData', purpose: 'FeatureInsight', }; + id: { classification: 'SystemMetaData', purpose: 'FeatureInsight', }; }; type EditorActionInvokedEvent = { name: string; @@ -241,7 +241,7 @@ export abstract class EditorAction extends EditorCommand { // --- Registration of commands and actions -export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: Args) => any) { +export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: Args) => any) { CommandsRegistry.registerCommand(id, (accessor, args) => handler(accessor, args || {})); } diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index 3cbd1cc286f..9057fdfbe22 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -13,6 +13,7 @@ import { IConfiguration } from 'vs/editor/common/editorCommon'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import * as platform from 'vs/base/common/platform'; export interface IMouseDispatchData { position: Position; @@ -132,7 +133,8 @@ export class ViewController { } public dispatchMouse(data: IMouseDispatchData): void { - if (data.middleButton) { + const selectionClipboardIsOn = (platform.isLinux && this.configuration.options.get(EditorOption.selectionClipboard)); + if (data.middleButton && !selectionClipboardIsOn) { this._columnSelect(data.position, data.mouseColumn, data.inSelectionMode); } else if (data.startedOnLineNumbers) { // If the dragging started on the gutter, then have operations work on the entire line diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index 3a962f22ea2..e98cea19e9c 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -9,7 +9,7 @@ import { ContentWidgetPositionPreference, IContentWidget } from 'vs/editor/brows import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; @@ -380,7 +380,7 @@ class Widget { belowLeft = absoluteBelowLeft; } - return { fitsAbove, aboveTop, aboveLeft, fitsBelow, belowTop, belowLeft }; + return { fitsAbove, aboveTop: Math.max(aboveTop, TOP_PADDING), aboveLeft, fitsBelow, belowTop, belowLeft }; } private _prepareRenderWidgetAtExactPositionOverflowing(topLeft: Coordinate): Coordinate { diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts index 5344effa706..f84a79df8d4 100644 --- a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; class FloatHorizontalRange { diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 4e0016d5fac..b3b0ac98942 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -16,7 +16,7 @@ import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine, import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; import { HIGH_CONTRAST, ThemeType } from 'vs/platform/theme/common/themeService'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { EditorOption, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; const canUseFastRenderedViewLine = (function () { if (platform.isNative) { @@ -77,7 +77,7 @@ export class ViewLineOptions { public readonly canUseHalfwidthRightwardsArrow: boolean; public readonly lineHeight: number; public readonly stopRenderingLineAfter: number; - public readonly fontLigatures: boolean; + public readonly fontLigatures: string; constructor(config: IConfiguration, themeType: ThemeType) { this.themeType = themeType; @@ -89,7 +89,6 @@ export class ViewLineOptions { this.useMonospaceOptimizations = ( fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations) - && !options.get(EditorOption.fontLigatures) ); this.canUseHalfwidthRightwardsArrow = fontInfo.canUseHalfwidthRightwardsArrow; this.lineHeight = options.get(EditorOption.lineHeight); @@ -218,7 +217,7 @@ export class ViewLine implements IVisibleLine { options.stopRenderingLineAfter, options.renderWhitespace, options.renderControlCharacters, - options.fontLigatures, + options.fontLigatures !== EditorFontLigatures.OFF, selectionsOnLine ); diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index eb524a41b69..ff3a504d5cf 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -20,6 +20,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { Viewport } from 'vs/editor/common/viewModel/viewModel'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Constants } from 'vs/base/common/uint'; class LastRenderedData { @@ -641,7 +642,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, const viewportEndX = viewportStartX + viewport.width; const visibleRanges = this.visibleRangesForRange2(new Range(lineNumber, startColumn, lineNumber, endColumn)); - let boxStartX = Number.MAX_VALUE; + let boxStartX = Constants.MAX_SAFE_SMALL_INTEGER; let boxEndX = 0; if (!visibleRanges) { diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 8914672b297..b32b8d83775 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -456,6 +456,7 @@ export class Minimap extends ViewPart { private readonly _mouseDownListener: IDisposable; private readonly _sliderMouseMoveMonitor: GlobalMouseMoveMonitor; private readonly _sliderMouseDownListener: IDisposable; + private readonly _gestureDisposable: IDisposable; private readonly _sliderTouchStartListener: IDisposable; private readonly _sliderTouchMoveListener: IDisposable; private readonly _sliderTouchEndListener: IDisposable; @@ -576,7 +577,7 @@ export class Minimap extends ViewPart { } }); - Gesture.addTarget(this._domNode.domNode); + this._gestureDisposable = Gesture.addTarget(this._domNode.domNode); this._sliderTouchStartListener = dom.addDisposableListener(this._domNode.domNode, EventType.Start, (e: GestureEvent) => { e.preventDefault(); e.stopPropagation(); @@ -615,6 +616,7 @@ export class Minimap extends ViewPart { this._mouseDownListener.dispose(); this._sliderMouseMoveMonitor.dispose(); this._sliderMouseDownListener.dispose(); + this._gestureDisposable.dispose(); this._sliderTouchStartListener.dispose(); this._sliderTouchMoveListener.dispose(); this._sliderTouchEndListener.dispose(); diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index a4e988745cb..9c1edf519e0 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -61,8 +61,8 @@ class Settings { const minimapOpts = options.get(EditorOption.minimap); const minimapEnabled = minimapOpts.enabled; const minimapSide = minimapOpts.side; - const backgroundColor = (minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null); - if (backgroundColor === null || minimapSide === 'left') { + const backgroundColor = (minimapEnabled ? TokenizationRegistry.getDefaultBackground() : undefined); + if (typeof backgroundColor === 'undefined' || minimapSide === 'left') { this.backgroundColor = null; } else { this.backgroundColor = Color.Format.CSS.formatHex(backgroundColor); diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index f059e95dbb1..fa2dd722033 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -278,13 +278,17 @@ export class SelectionsOverlay extends DynamicViewOverlay { ); } - private _actualRenderOneSelection(output2: string[], visibleStartLineNumber: number, hasMultipleSelections: boolean, visibleRanges: LineVisibleRangesWithStyle[]): void { - const visibleRangesHaveStyle = (visibleRanges.length > 0 && visibleRanges[0].ranges[0].startStyle); + private _actualRenderOneSelection(output2: [string, string][], visibleStartLineNumber: number, hasMultipleSelections: boolean, visibleRanges: LineVisibleRangesWithStyle[]): void { + if (visibleRanges.length === 0) { + return; + } + + const visibleRangesHaveStyle = !!visibleRanges[0].ranges[0].startStyle; const fullLineHeight = (this._lineHeight).toString(); const reducedLineHeight = (this._lineHeight - 1).toString(); - const firstLineNumber = (visibleRanges.length > 0 ? visibleRanges[0].lineNumber : 0); - const lastLineNumber = (visibleRanges.length > 0 ? visibleRanges[visibleRanges.length - 1].lineNumber : 0); + const firstLineNumber = visibleRanges[0].lineNumber; + const lastLineNumber = visibleRanges[visibleRanges.length - 1].lineNumber; for (let i = 0, len = visibleRanges.length; i < len; i++) { const lineVisibleRanges = visibleRanges[i]; @@ -294,7 +298,8 @@ export class SelectionsOverlay extends DynamicViewOverlay { const lineHeight = hasMultipleSelections ? (lineNumber === lastLineNumber || lineNumber === firstLineNumber ? reducedLineHeight : fullLineHeight) : fullLineHeight; const top = hasMultipleSelections ? (lineNumber === firstLineNumber ? 1 : 0) : 0; - let lineOutput = ''; + let innerCornerOutput = ''; + let restOfSelectionOutput = ''; for (let j = 0, lenJ = lineVisibleRanges.ranges.length; j < lenJ; j++) { const visibleRange = lineVisibleRanges.ranges[j]; @@ -306,7 +311,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { // Reverse rounded corner to the left // First comes the selection (blue layer) - lineOutput += this._createSelectionPiece(top, lineHeight, SelectionsOverlay.SELECTION_CLASS_NAME, visibleRange.left - SelectionsOverlay.ROUNDED_PIECE_WIDTH, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + innerCornerOutput += this._createSelectionPiece(top, lineHeight, SelectionsOverlay.SELECTION_CLASS_NAME, visibleRange.left - SelectionsOverlay.ROUNDED_PIECE_WIDTH, SelectionsOverlay.ROUNDED_PIECE_WIDTH); // Second comes the background (white layer) with inverse border radius let className = SelectionsOverlay.EDITOR_BACKGROUND_CLASS_NAME; @@ -316,13 +321,13 @@ export class SelectionsOverlay extends DynamicViewOverlay { if (startStyle.bottom === CornerStyle.INTERN) { className += ' ' + SelectionsOverlay.SELECTION_BOTTOM_RIGHT; } - lineOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left - SelectionsOverlay.ROUNDED_PIECE_WIDTH, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + innerCornerOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left - SelectionsOverlay.ROUNDED_PIECE_WIDTH, SelectionsOverlay.ROUNDED_PIECE_WIDTH); } if (endStyle.top === CornerStyle.INTERN || endStyle.bottom === CornerStyle.INTERN) { // Reverse rounded corner to the right // First comes the selection (blue layer) - lineOutput += this._createSelectionPiece(top, lineHeight, SelectionsOverlay.SELECTION_CLASS_NAME, visibleRange.left + visibleRange.width, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + innerCornerOutput += this._createSelectionPiece(top, lineHeight, SelectionsOverlay.SELECTION_CLASS_NAME, visibleRange.left + visibleRange.width, SelectionsOverlay.ROUNDED_PIECE_WIDTH); // Second comes the background (white layer) with inverse border radius let className = SelectionsOverlay.EDITOR_BACKGROUND_CLASS_NAME; @@ -332,7 +337,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { if (endStyle.bottom === CornerStyle.INTERN) { className += ' ' + SelectionsOverlay.SELECTION_BOTTOM_LEFT; } - lineOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left + visibleRange.width, SelectionsOverlay.ROUNDED_PIECE_WIDTH); + innerCornerOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left + visibleRange.width, SelectionsOverlay.ROUNDED_PIECE_WIDTH); } } @@ -353,22 +358,26 @@ export class SelectionsOverlay extends DynamicViewOverlay { className += ' ' + SelectionsOverlay.SELECTION_BOTTOM_RIGHT; } } - lineOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left, visibleRange.width); + restOfSelectionOutput += this._createSelectionPiece(top, lineHeight, className, visibleRange.left, visibleRange.width); } - output2[lineIndex] += lineOutput; + output2[lineIndex][0] += innerCornerOutput; + output2[lineIndex][1] += restOfSelectionOutput; } } private _previousFrameVisibleRangesWithStyle: (LineVisibleRangesWithStyle[] | null)[] = []; public prepareRender(ctx: RenderingContext): void { - const output: string[] = []; + // Build HTML for inner corners separate from HTML for the the rest of selections, + // as the inner corner HTML can interfere with that of other selections. + // In final render, make sure to place the inner corner HTML before the rest of selection HTML. See issue #77777. + const output: [string, string][] = []; const visibleStartLineNumber = ctx.visibleRange.startLineNumber; const visibleEndLineNumber = ctx.visibleRange.endLineNumber; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { const lineIndex = lineNumber - visibleStartLineNumber; - output[lineIndex] = ''; + output[lineIndex] = ['', '']; } const thisFrameVisibleRangesWithStyle: (LineVisibleRangesWithStyle[] | null)[] = []; @@ -385,7 +394,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { } this._previousFrameVisibleRangesWithStyle = thisFrameVisibleRangesWithStyle; - this._renderResult = output; + this._renderResult = output.map(([internalCorners, restOfSelection]) => internalCorners + restOfSelection); } public render(startLineNumber: number, lineNumber: number): string { diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index c60e020b56f..a703cbafd2b 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -18,7 +18,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic export class ViewCursors extends ViewPart { - static BLINK_INTERVAL = 500; + static readonly BLINK_INTERVAL = 500; private _readOnly: boolean; private _cursorBlinking: TextEditorCursorBlinkingStyle; diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ef815de2219..90f3acdf3cc 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -20,7 +20,7 @@ import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffReview } from 'vs/editor/browser/widget/diffReview'; -import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, IComputedEditorOptions, EditorOption, EditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, IComputedEditorOptions, EditorOption, EditorOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; @@ -43,6 +43,7 @@ import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { Constants } from 'vs/base/common/uint'; interface IEditorDiffDecorations { decorations: IModelDeltaDecoration[]; @@ -491,6 +492,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } })); + this._register(editor.onDidChangeModelOptions((e) => { + if (e.tabSize) { + this._updateDecorationsRunner.schedule(); + } + })); + return editor; } @@ -646,6 +653,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.setModel(model ? model.modified : null); this._updateDecorationsRunner.cancel(); + // this.originalEditor.onDidChangeModelOptions + if (model) { this.originalEditor.setScrollTop(0); this.modifiedEditor.setScrollTop(0); @@ -989,7 +998,6 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _adjustOptionsForLeftHandSide(options: IDiffEditorOptions, isEditable: boolean): IEditorOptions { let result = this._adjustOptionsForSubEditor(options); result.readOnly = !isEditable; - result.overviewRulerLanes = 1; result.extraEditorClassName = 'original-in-monaco-diff-editor'; return result; } @@ -1122,7 +1130,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE while (min < max) { let mid = Math.floor((min + max) / 2); let midStart = startLineNumberExtractor(lineChanges[mid]); - let midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Number.MAX_VALUE); + let midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Constants.MAX_SAFE_SMALL_INTEGER); if (lineNumber < midStart) { max = mid - 1; @@ -1562,7 +1570,7 @@ const DECORATIONS = { class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider { - static MINIMUM_EDITOR_WIDTH = 100; + static readonly MINIMUM_EDITOR_WIDTH = 100; private _disableSash: boolean; private readonly _sash: Sash; @@ -1682,11 +1690,11 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE if (isChangeOrDelete(lineChange)) { result.decorations.push({ - range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE), + range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), options: (renderIndicators ? DECORATIONS.lineDeleteWithSign : DECORATIONS.lineDelete) }); if (!isChangeOrInsert(lineChange) || !lineChange.charChanges) { - result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE, DECORATIONS.charDeleteWholeLine)); + result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charDeleteWholeLine)); } result.overviewZones.push(new OverviewRulerZone( @@ -1743,11 +1751,11 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE if (isChangeOrInsert(lineChange)) { result.decorations.push({ - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE), + range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) }); if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) { - result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine)); + result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charInsertWholeLine)); } result.overviewZones.push(new OverviewRulerZone( lineChange.modifiedStartLineNumber, @@ -1861,7 +1869,7 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito // Add overview zones in the overview ruler if (isChangeOrDelete(lineChange)) { result.decorations.push({ - range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Number.MAX_VALUE), + range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), options: DECORATIONS.lineDeleteMargin }); @@ -1892,7 +1900,7 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito // Add decorations & overview zones if (isChangeOrInsert(lineChange)) { result.decorations.push({ - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE), + range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) }); @@ -1928,7 +1936,7 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito } } } else { - result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Number.MAX_VALUE, DECORATIONS.charInsertWholeLine)); + result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charInsertWholeLine)); } } } @@ -2060,7 +2068,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, originalModel.mightContainNonBasicASCII()); const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, originalModel.mightContainRTL()); const output = renderViewLine(new RenderLineInput( - (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations) && !options.get(EditorOption.fontLigatures)), + (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)), fontInfo.canUseHalfwidthRightwardsArrow, lineContent, false, @@ -2074,7 +2082,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), - options.get(EditorOption.fontLigatures), + options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF, null // Send no selections, original line cannot be selected ), sb); diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 0f71723805c..8a915a659ff 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -17,7 +17,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IComputedEditorOptions, EditorOption, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; import { ILineChange, ScrollType } from 'vs/editor/common/editorCommon'; @@ -770,7 +770,7 @@ export class DiffReview extends Disposable { const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, model.mightContainNonBasicASCII()); const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, model.mightContainRTL()); const r = renderViewLine(new RenderLineInput( - (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations) && !options.get(EditorOption.fontLigatures)), + (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)), fontInfo.canUseHalfwidthRightwardsArrow, lineContent, false, @@ -784,7 +784,7 @@ export class DiffReview extends Disposable { options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), - options.get(EditorOption.fontLigatures), + options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF, null )); diff --git a/src/vs/editor/browser/widget/media/editor.css b/src/vs/editor/browser/widget/media/editor.css index fb9e5f5e8b8..7936ff11ad8 100644 --- a/src/vs/editor/browser/widget/media/editor.css +++ b/src/vs/editor/browser/widget/media/editor.css @@ -21,12 +21,6 @@ position: relative; overflow: visible; -webkit-text-size-adjust: 100%; - -webkit-font-feature-settings: "liga" off, "calt" off; - font-feature-settings: "liga" off, "calt" off; -} -.monaco-editor.enable-ligatures { - -webkit-font-feature-settings: "liga" on, "calt" on; - font-feature-settings: "liga" on, "calt" on; } /* -------------------- Misc -------------------- */ diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index af4f2074ea8..f017f7e5fcf 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -207,6 +207,17 @@ function migrateOptions(options: IEditorOptions): void { enabled: false }; } + + const parameterHints = options.parameterHints; + if (parameterHints === true) { + options.parameterHints = { + enabled: true + }; + } else if (parameterHints === false) { + options.parameterHints = { + enabled: false + }; + } } function deepCloneAndMigrateOptions(_options: IEditorOptions): IEditorOptions { @@ -293,18 +304,6 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed return EditorConfiguration2.computeOptions(this._validatedOptions, env); } - private static _primitiveArrayEquals(a: any[], b: any[]): boolean { - if (a.length !== b.length) { - return false; - } - for (let i = 0; i < a.length; i++) { - if (a[i] !== b[i]) { - return false; - } - } - return true; - } - private static _subsetEquals(base: { [key: string]: any }, subset: { [key: string]: any }): boolean { for (const key in subset) { if (hasOwnProperty.call(subset, key)) { @@ -315,7 +314,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed continue; } if (Array.isArray(baseValue) && Array.isArray(subsetValue)) { - if (!this._primitiveArrayEquals(baseValue, subsetValue)) { + if (!arrays.equals(baseValue, subsetValue)) { return false; } continue; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index f4c6c2df7f9..44a7959ddcf 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { isObject } from 'vs/base/common/types'; @@ -178,7 +178,7 @@ export interface IEditorOptions { * Enable font ligatures. * Defaults to false. */ - fontLigatures?: boolean; + fontLigatures?: boolean | string; /** * Disable the use of `will-change` for the editor margin and lines layers. * The usage of `will-change` acts as a hint for browsers to create an extra layer. @@ -641,6 +641,9 @@ export interface IEditorOption { type PossibleKeyName0 = { [K in keyof IEditorOptions]: IEditorOptions[K] extends V | undefined ? K : never }[keyof IEditorOptions]; type PossibleKeyName = NonNullable>; +/** + * @internal + */ abstract class BaseEditorOption implements IEditorOption { public readonly id: K1; @@ -1044,7 +1047,7 @@ function _cursorStyleFromString(cursorStyle: 'line' | 'block' | 'underline' | 'l class EditorClassName extends ComputedEditorOption { constructor() { - super(EditorOption.editorClassName, [EditorOption.mouseStyle, EditorOption.fontLigatures, EditorOption.extraEditorClassName]); + super(EditorOption.editorClassName, [EditorOption.mouseStyle, EditorOption.extraEditorClassName]); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: string): string { @@ -1055,9 +1058,6 @@ class EditorClassName extends ComputedEditorOption //#endregion +//#region fontLigatures + +/** + * @internal + */ +export class EditorFontLigatures extends BaseEditorOption { + + public static OFF = '"liga" off, "calt" off'; + public static ON = '"liga" on, "calt" on'; + + constructor() { + super( + EditorOption.fontLigatures, 'fontLigatures', EditorFontLigatures.OFF, + { + anyOf: [ + { + type: 'boolean', + description: nls.localize('fontLigatures', "Enables/Disables font ligatures."), + }, + { + type: 'string', + description: nls.localize('fontFeatureSettings', "Explicit font-feature-settings.") + } + ], + default: false + } + ); + } + + public validate(input: any): string { + if (typeof input === 'undefined') { + return this.defaultValue; + } + if (typeof input === 'string') { + if (input === 'false') { + return EditorFontLigatures.OFF; + } + if (input === 'true') { + return EditorFontLigatures.ON; + } + return input; + } + if (Boolean(input)) { + return EditorFontLigatures.ON; + } + return EditorFontLigatures.OFF; + } +} + +//#endregion + //#region fontInfo class EditorFontInfo extends ComputedEditorOption { @@ -1782,7 +1833,7 @@ class EditorMinimap extends BaseEditorOption event.type === viewEvents.ViewEventType.ViewLineMappingChanged); } export class CursorStateChangedEvent { @@ -73,15 +69,7 @@ export class CursorModelState { if (this.modelVersionId !== other.modelVersionId) { return false; } - if (this.cursorState.length !== other.cursorState.length) { - return false; - } - for (let i = 0, len = this.cursorState.length; i < len; i++) { - if (!this.cursorState[i].equals(other.cursorState[i])) { - return false; - } - } - return true; + return equals(this.cursorState, other.cursorState, (a, b) => a.equals(b)); } } @@ -153,7 +141,7 @@ class AutoClosedAction { export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { - public static MAX_CURSOR_COUNT = 10000; + public static readonly MAX_CURSOR_COUNT = 10000; private readonly _onDidReachMaxCursorCount: Emitter = this._register(new Emitter()); public readonly onDidReachMaxCursorCount: Event = this._onDidReachMaxCursorCount.event; @@ -949,12 +937,7 @@ class CommandExecutor { } private static _arrayIsEmpty(commands: (editorCommon.ICommand | null)[]): boolean { - for (let i = 0, len = commands.length; i < len; i++) { - if (commands[i]) { - return false; - } - } - return true; + return commands.every(command => !command); } private static _getEditOperations(ctx: IExecContext, commands: (editorCommon.ICommand | null)[]): ICommandsData { diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 9fc69bc842e..0df564775fd 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -81,15 +81,12 @@ export class TypeOperations { const selection = selections[i]; let position = selection.getPosition(); + if (pasteOnNewLine && !selection.isEmpty()) { + pasteOnNewLine = false; + } if (pasteOnNewLine && text.indexOf('\n') !== text.length - 1) { pasteOnNewLine = false; } - if (pasteOnNewLine && selection.startLineNumber !== selection.endLineNumber) { - pasteOnNewLine = false; - } - if (pasteOnNewLine && selection.startColumn === model.getLineMinColumn(selection.startLineNumber) && selection.endColumn === model.getLineMaxColumn(selection.startLineNumber)) { - pasteOnNewLine = false; - } if (pasteOnNewLine) { // Paste entire line at the beginning of line diff --git a/src/vs/editor/common/core/characterClassifier.ts b/src/vs/editor/common/core/characterClassifier.ts index 9d448641d17..214bb2abf05 100644 --- a/src/vs/editor/common/core/characterClassifier.ts +++ b/src/vs/editor/common/core/characterClassifier.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toUint8 } from 'vs/editor/common/core/uint'; +import { toUint8 } from 'vs/base/common/uint'; /** * A fast character classifier that uses a compact array for ASCII values. diff --git a/src/vs/editor/common/core/rgba.ts b/src/vs/editor/common/core/rgba.ts index 0ce94359f31..3a19425bc4d 100644 --- a/src/vs/editor/common/core/rgba.ts +++ b/src/vs/editor/common/core/rgba.ts @@ -10,7 +10,7 @@ export class RGBA8 { _rgba8Brand: void; - static Empty = new RGBA8(0, 0, 0, 0); + static readonly Empty = new RGBA8(0, 0, 0, 0); /** * Red: integer in [0-255] diff --git a/src/vs/editor/common/core/selection.ts b/src/vs/editor/common/core/selection.ts index 80143cdf97a..b1776654e94 100644 --- a/src/vs/editor/common/core/selection.ts +++ b/src/vs/editor/common/core/selection.ts @@ -5,6 +5,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; +import { equals } from 'vs/base/common/arrays'; /** * A selection in the editor. @@ -171,15 +172,7 @@ export class Selection extends Range { if (!a && !b) { return true; } - if (a.length !== b.length) { - return false; - } - for (let i = 0, len = a.length; i < len; i++) { - if (!this.selectionsEqual(a[i], b[i])) { - return false; - } - } - return true; + return equals(a, b, this.selectionsEqual); } /** diff --git a/src/vs/editor/common/diff/diffComputer.ts b/src/vs/editor/common/diff/diffComputer.ts index 67ccd7e5c4e..362ce26d72b 100644 --- a/src/vs/editor/common/diff/diffComputer.ts +++ b/src/vs/editor/common/diff/diffComputer.ts @@ -15,9 +15,9 @@ function computeDiff(originalSequence: ISequence, modifiedSequence: ISequence, c return diffAlgo.ComputeDiff(pretty); } -class LineMarkerSequence implements ISequence { +class LineSequence implements ISequence { - private readonly _lines: string[]; + public readonly lines: string[]; private readonly _startColumns: number[]; private readonly _endColumns: number[]; @@ -25,61 +25,37 @@ class LineMarkerSequence implements ISequence { let startColumns: number[] = []; let endColumns: number[] = []; for (let i = 0, length = lines.length; i < length; i++) { - startColumns[i] = LineMarkerSequence._getFirstNonBlankColumn(lines[i], 1); - endColumns[i] = LineMarkerSequence._getLastNonBlankColumn(lines[i], 1); + startColumns[i] = getFirstNonBlankColumn(lines[i], 1); + endColumns[i] = getLastNonBlankColumn(lines[i], 1); } - this._lines = lines; + this.lines = lines; this._startColumns = startColumns; this._endColumns = endColumns; } - public getLength(): number { - return this._lines.length; - } - - public getElementAtIndex(i: number): string { - return this._lines[i].substring(this._startColumns[i] - 1, this._endColumns[i] - 1); + public getElements(): Int32Array | number[] | string[] { + const elements: string[] = []; + for (let i = 0, len = this.lines.length; i < len; i++) { + elements[i] = this.lines[i].substring(this._startColumns[i] - 1, this._endColumns[i] - 1); + } + return elements; } public getStartLineNumber(i: number): number { return i + 1; } - public getStartColumn(i: number): number { - return this._startColumns[i]; - } - public getEndLineNumber(i: number): number { return i + 1; } - public getEndColumn(i: number): number { - return this._endColumns[i]; - } - - public static _getFirstNonBlankColumn(txt: string, defaultValue: number): number { - const r = strings.firstNonWhitespaceIndex(txt); - if (r === -1) { - return defaultValue; - } - return r + 1; - } - - public static _getLastNonBlankColumn(txt: string, defaultValue: number): number { - const r = strings.lastNonWhitespaceIndex(txt); - if (r === -1) { - return defaultValue; - } - return r + 2; - } - - public getCharSequence(shouldIgnoreTrimWhitespace: boolean, startIndex: number, endIndex: number): CharSequence { + public createCharSequence(shouldIgnoreTrimWhitespace: boolean, startIndex: number, endIndex: number): CharSequence { let charCodes: number[] = []; let lineNumbers: number[] = []; let columns: number[] = []; let len = 0; for (let index = startIndex; index <= endIndex; index++) { - const lineContent = this._lines[index]; + const lineContent = this.lines[index]; const startColumn = (shouldIgnoreTrimWhitespace ? this._startColumns[index] : 1); const endColumn = (shouldIgnoreTrimWhitespace ? this._endColumns[index] : lineContent.length + 1); for (let col = startColumn; col < endColumn; col++) { @@ -105,12 +81,8 @@ class CharSequence implements ISequence { this._columns = columns; } - public getLength(): number { - return this._charCodes.length; - } - - public getElementAtIndex(i: number): number { - return this._charCodes[i]; + public getElements(): Int32Array | number[] | string[] { + return this._charCodes; } public getStartLineNumber(i: number): number { @@ -254,7 +226,7 @@ class LineChange implements ILineChange { this.charChanges = charChanges; } - public static createFromDiffResult(shouldIgnoreTrimWhitespace: boolean, diffChange: IDiffChange, originalLineSequence: LineMarkerSequence, modifiedLineSequence: LineMarkerSequence, continueProcessingPredicate: () => boolean, shouldComputeCharChanges: boolean, shouldPostProcessCharChanges: boolean): LineChange { + public static createFromDiffResult(shouldIgnoreTrimWhitespace: boolean, diffChange: IDiffChange, originalLineSequence: LineSequence, modifiedLineSequence: LineSequence, continueProcessingPredicate: () => boolean, shouldComputeCharChanges: boolean, shouldPostProcessCharChanges: boolean): LineChange { let originalStartLineNumber: number; let originalEndLineNumber: number; let modifiedStartLineNumber: number; @@ -277,9 +249,10 @@ class LineChange implements ILineChange { modifiedEndLineNumber = modifiedLineSequence.getEndLineNumber(diffChange.modifiedStart + diffChange.modifiedLength - 1); } - if (shouldComputeCharChanges && diffChange.originalLength !== 0 && diffChange.modifiedLength !== 0 && continueProcessingPredicate()) { - const originalCharSequence = originalLineSequence.getCharSequence(shouldIgnoreTrimWhitespace, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength - 1); - const modifiedCharSequence = modifiedLineSequence.getCharSequence(shouldIgnoreTrimWhitespace, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength - 1); + if (shouldComputeCharChanges && diffChange.originalLength > 0 && diffChange.originalLength < 20 && diffChange.modifiedLength > 0 && diffChange.modifiedLength < 20 && continueProcessingPredicate()) { + // Compute character changes for diff chunks of at most 20 lines... + const originalCharSequence = originalLineSequence.createCharSequence(shouldIgnoreTrimWhitespace, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength - 1); + const modifiedCharSequence = modifiedLineSequence.createCharSequence(shouldIgnoreTrimWhitespace, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength - 1); let rawChanges = computeDiff(originalCharSequence, modifiedCharSequence, continueProcessingPredicate, true); @@ -313,8 +286,8 @@ export class DiffComputer { private readonly maximumRunTimeMs: number; private readonly originalLines: string[]; private readonly modifiedLines: string[]; - private readonly original: LineMarkerSequence; - private readonly modified: LineMarkerSequence; + private readonly original: LineSequence; + private readonly modified: LineSequence; private computationStartTime: number; @@ -326,21 +299,21 @@ export class DiffComputer { this.maximumRunTimeMs = MAXIMUM_RUN_TIME; this.originalLines = originalLines; this.modifiedLines = modifiedLines; - this.original = new LineMarkerSequence(originalLines); - this.modified = new LineMarkerSequence(modifiedLines); + this.original = new LineSequence(originalLines); + this.modified = new LineSequence(modifiedLines); this.computationStartTime = (new Date()).getTime(); } public computeDiff(): ILineChange[] { - if (this.original.getLength() === 1 && this.original.getElementAtIndex(0).length === 0) { + if (this.original.lines.length === 1 && this.original.lines[0].length === 0) { // empty original => fast path return [{ originalStartLineNumber: 1, originalEndLineNumber: 1, modifiedStartLineNumber: 1, - modifiedEndLineNumber: this.modified.getLength(), + modifiedEndLineNumber: this.modified.lines.length, charChanges: [{ modifiedEndColumn: 0, modifiedEndLineNumber: 0, @@ -354,11 +327,11 @@ export class DiffComputer { }]; } - if (this.modified.getLength() === 1 && this.modified.getElementAtIndex(0).length === 0) { + if (this.modified.lines.length === 1 && this.modified.lines[0].length === 0) { // empty modified => fast path return [{ originalStartLineNumber: 1, - originalEndLineNumber: this.original.getLength(), + originalEndLineNumber: this.original.lines.length, modifiedStartLineNumber: 1, modifiedEndLineNumber: 1, charChanges: [{ @@ -409,8 +382,8 @@ export class DiffComputer { // Check the leading whitespace { - let originalStartColumn = LineMarkerSequence._getFirstNonBlankColumn(originalLine, 1); - let modifiedStartColumn = LineMarkerSequence._getFirstNonBlankColumn(modifiedLine, 1); + let originalStartColumn = getFirstNonBlankColumn(originalLine, 1); + let modifiedStartColumn = getFirstNonBlankColumn(modifiedLine, 1); while (originalStartColumn > 1 && modifiedStartColumn > 1) { const originalChar = originalLine.charCodeAt(originalStartColumn - 2); const modifiedChar = modifiedLine.charCodeAt(modifiedStartColumn - 2); @@ -431,8 +404,8 @@ export class DiffComputer { // Check the trailing whitespace { - let originalEndColumn = LineMarkerSequence._getLastNonBlankColumn(originalLine, 1); - let modifiedEndColumn = LineMarkerSequence._getLastNonBlankColumn(modifiedLine, 1); + let originalEndColumn = getLastNonBlankColumn(originalLine, 1); + let modifiedEndColumn = getLastNonBlankColumn(modifiedLine, 1); const originalMaxColumn = originalLine.length + 1; const modifiedMaxColumn = modifiedLine.length + 1; while (originalEndColumn < originalMaxColumn && modifiedEndColumn < modifiedMaxColumn) { @@ -534,3 +507,19 @@ export class DiffComputer { } } + +function getFirstNonBlankColumn(txt: string, defaultValue: number): number { + const r = strings.firstNonWhitespaceIndex(txt); + if (r === -1) { + return defaultValue; + } + return r + 1; +} + +function getLastNonBlankColumn(txt: string, defaultValue: number): number { + const r = strings.lastNonWhitespaceIndex(txt); + if (r === -1) { + return defaultValue; + } + return r + 2; +} diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index f350079ee69..4622af82cbc 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -85,7 +85,7 @@ export function isMultilineRegexSource(searchString: string): boolean { } const nextChCode = searchString.charCodeAt(i); - if (nextChCode === CharCode.n || nextChCode === CharCode.r || nextChCode === CharCode.W) { + if (nextChCode === CharCode.n || nextChCode === CharCode.r || nextChCode === CharCode.W || nextChCode === CharCode.w) { return true; } } diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 03e1c4bd6b7..da1fc0abfbb 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -16,6 +16,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { Disposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; import { MultilineTokensBuilder, countEOL } from 'vs/editor/common/model/tokensStore'; +import * as platform from 'vs/base/common/platform'; const enum Constants { CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048 @@ -196,15 +197,14 @@ export class TextModelTokenization extends Disposable { private readonly _textModel: TextModel; private readonly _tokenizationStateStore: TokenizationStateStore; - private _revalidateTokensTimeout: any; - private _tokenizationSupport: ITokenizationSupport | null; + private _isDisposed: boolean; + private _tokenizationSupport: ITokenizationSupport | undefined; constructor(textModel: TextModel) { super(); + this._isDisposed = false; this._textModel = textModel; this._tokenizationStateStore = new TokenizationStateStore(); - this._revalidateTokensTimeout = -1; - this._tokenizationSupport = null; this._register(TokenizationRegistry.onDidChange((e) => { const languageIdentifier = this._textModel.getLanguageIdentifier(); @@ -246,19 +246,11 @@ export class TextModelTokenization extends Disposable { } public dispose(): void { - this._clearTimers(); + this._isDisposed = true; super.dispose(); } - private _clearTimers(): void { - if (this._revalidateTokensTimeout !== -1) { - clearTimeout(this._revalidateTokensTimeout); - this._revalidateTokensTimeout = -1; - } - } - private _resetTokenizationState(): void { - this._clearTimers(); const [tokenizationSupport, initialState] = initializeTokenization(this._textModel); this._tokenizationSupport = tokenizationSupport; this._tokenizationStateStore.flush(initialState); @@ -266,16 +258,19 @@ export class TextModelTokenization extends Disposable { } private _beginBackgroundTokenization(): void { - if (this._textModel.isAttachedToEditor() && this._hasLinesToTokenize() && this._revalidateTokensTimeout === -1) { - this._revalidateTokensTimeout = setTimeout(() => { - this._revalidateTokensTimeout = -1; + if (this._textModel.isAttachedToEditor() && this._hasLinesToTokenize()) { + platform.setImmediate(() => { + if (this._isDisposed) { + // disposed in the meantime + return; + } this._revalidateTokensNow(); - }, 0); + }); } } private _revalidateTokensNow(toLineNumber: number = this._textModel.getLineCount()): void { - const MAX_ALLOWED_TIME = 20; + const MAX_ALLOWED_TIME = 1; const builder = new MultilineTokensBuilder(); const sw = StopWatch.create(false); @@ -428,11 +423,11 @@ export class TextModelTokenization extends Disposable { } } -function initializeTokenization(textModel: TextModel): [ITokenizationSupport | null, IState | null] { +function initializeTokenization(textModel: TextModel): [ITokenizationSupport | undefined, IState | null] { const languageIdentifier = textModel.getLanguageIdentifier(); let tokenizationSupport = ( textModel.isTooLargeForTokenization() - ? null + ? undefined : TokenizationRegistry.get(languageIdentifier.language) ); let initialState: IState | null = null; @@ -441,7 +436,7 @@ function initializeTokenization(textModel: TextModel): [ITokenizationSupport | n initialState = tokenizationSupport.getInitialState(); } catch (e) { onUnexpectedError(e); - tokenizationSupport = null; + tokenizationSupport = undefined; } } return [tokenizationSupport, initialState]; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index ede4308bd87..f7b1ea578a4 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -19,6 +19,7 @@ import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureR import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; +import { keys } from 'vs/base/common/map'; /** * Open ended enum at runtime @@ -874,40 +875,88 @@ export const enum SymbolTag { /** * @internal */ -export const symbolKindToCssClass = (function () { +export namespace SymbolKinds { - const _fromMapping: { [n: number]: string } = Object.create(null); - _fromMapping[SymbolKind.File] = 'file'; - _fromMapping[SymbolKind.Module] = 'module'; - _fromMapping[SymbolKind.Namespace] = 'namespace'; - _fromMapping[SymbolKind.Package] = 'package'; - _fromMapping[SymbolKind.Class] = 'class'; - _fromMapping[SymbolKind.Method] = 'method'; - _fromMapping[SymbolKind.Property] = 'property'; - _fromMapping[SymbolKind.Field] = 'field'; - _fromMapping[SymbolKind.Constructor] = 'constructor'; - _fromMapping[SymbolKind.Enum] = 'enum'; - _fromMapping[SymbolKind.Interface] = 'interface'; - _fromMapping[SymbolKind.Function] = 'function'; - _fromMapping[SymbolKind.Variable] = 'variable'; - _fromMapping[SymbolKind.Constant] = 'constant'; - _fromMapping[SymbolKind.String] = 'string'; - _fromMapping[SymbolKind.Number] = 'number'; - _fromMapping[SymbolKind.Boolean] = 'boolean'; - _fromMapping[SymbolKind.Array] = 'array'; - _fromMapping[SymbolKind.Object] = 'object'; - _fromMapping[SymbolKind.Key] = 'key'; - _fromMapping[SymbolKind.Null] = 'null'; - _fromMapping[SymbolKind.EnumMember] = 'enum-member'; - _fromMapping[SymbolKind.Struct] = 'struct'; - _fromMapping[SymbolKind.Event] = 'event'; - _fromMapping[SymbolKind.Operator] = 'operator'; - _fromMapping[SymbolKind.TypeParameter] = 'type-parameter'; + const byName = new Map(); + byName.set('file', SymbolKind.File); + byName.set('module', SymbolKind.Module); + byName.set('namespace', SymbolKind.Namespace); + byName.set('package', SymbolKind.Package); + byName.set('class', SymbolKind.Class); + byName.set('method', SymbolKind.Method); + byName.set('property', SymbolKind.Property); + byName.set('field', SymbolKind.Field); + byName.set('constructor', SymbolKind.Constructor); + byName.set('enum', SymbolKind.Enum); + byName.set('interface', SymbolKind.Interface); + byName.set('function', SymbolKind.Function); + byName.set('variable', SymbolKind.Variable); + byName.set('constant', SymbolKind.Constant); + byName.set('string', SymbolKind.String); + byName.set('number', SymbolKind.Number); + byName.set('boolean', SymbolKind.Boolean); + byName.set('array', SymbolKind.Array); + byName.set('object', SymbolKind.Object); + byName.set('key', SymbolKind.Key); + byName.set('null', SymbolKind.Null); + byName.set('enum-member', SymbolKind.EnumMember); + byName.set('struct', SymbolKind.Struct); + byName.set('event', SymbolKind.Event); + byName.set('operator', SymbolKind.Operator); + byName.set('type-parameter', SymbolKind.TypeParameter); - return function toCssClassName(kind: SymbolKind, inline?: boolean): string { - return `symbol-icon ${inline ? 'inline' : 'block'} ${_fromMapping[kind] || 'property'}`; - }; -})(); + const byKind = new Map(); + byKind.set(SymbolKind.File, 'file'); + byKind.set(SymbolKind.Module, 'module'); + byKind.set(SymbolKind.Namespace, 'namespace'); + byKind.set(SymbolKind.Package, 'package'); + byKind.set(SymbolKind.Class, 'class'); + byKind.set(SymbolKind.Method, 'method'); + byKind.set(SymbolKind.Property, 'property'); + byKind.set(SymbolKind.Field, 'field'); + byKind.set(SymbolKind.Constructor, 'constructor'); + byKind.set(SymbolKind.Enum, 'enum'); + byKind.set(SymbolKind.Interface, 'interface'); + byKind.set(SymbolKind.Function, 'function'); + byKind.set(SymbolKind.Variable, 'variable'); + byKind.set(SymbolKind.Constant, 'constant'); + byKind.set(SymbolKind.String, 'string'); + byKind.set(SymbolKind.Number, 'number'); + byKind.set(SymbolKind.Boolean, 'boolean'); + byKind.set(SymbolKind.Array, 'array'); + byKind.set(SymbolKind.Object, 'object'); + byKind.set(SymbolKind.Key, 'key'); + byKind.set(SymbolKind.Null, 'null'); + byKind.set(SymbolKind.EnumMember, 'enum-member'); + byKind.set(SymbolKind.Struct, 'struct'); + byKind.set(SymbolKind.Event, 'event'); + byKind.set(SymbolKind.Operator, 'operator'); + byKind.set(SymbolKind.TypeParameter, 'type-parameter'); + /** + * @internal + */ + export function fromString(value: string): SymbolKind | undefined { + return byName.get(value); + } + /** + * @internal + */ + export function names(): readonly string[] { + return keys(byName); + } + /** + * @internal + */ + export function toString(kind: SymbolKind): string | undefined { + return byKind.get(kind); + } + /** + * @internal + */ + export function toCssClassName(kind: SymbolKind, inline?: boolean): string { + return `symbol-icon ${inline ? 'inline' : 'block'} ${byKind.get(kind) || 'property'}`; + } +} export interface DocumentSymbol { name: string; @@ -1568,9 +1617,9 @@ export interface ITokenizationRegistry { /** * Get the tokenization support for a language. - * Returns `null` if not found. + * Returns `undefined` if not found. */ - get(language: string): ITokenizationSupport | null; + get(language: string): ITokenizationSupport | undefined; /** * Get the promise of a tokenization support for a language. @@ -1583,9 +1632,9 @@ export interface ITokenizationRegistry { */ setColorMap(colorMap: Color[]): void; - getColorMap(): Color[] | null; + getColorMap(): Color[] | undefined; - getDefaultBackground(): Color | null; + getDefaultBackground(): Color | undefined; } /** diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index ed8d2fdb4ae..686c7a0dab2 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -247,7 +247,7 @@ export class StandardAutoClosingPairConditional { if (Array.isArray(source.notIn)) { for (let i = 0, len = source.notIn.length; i < len; i++) { - let notIn = source.notIn[i]; + const notIn: string = source.notIn[i]; switch (notIn) { case 'string': this._standardTokenMask |= StandardTokenType.String; diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index 00bf7d01b36..6673c04f64a 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -5,7 +5,6 @@ import { CharCode } from 'vs/base/common/charCode'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { Uint8Matrix } from 'vs/editor/common/core/uint'; import { ILink } from 'vs/editor/common/modes'; export interface ILinkComputerTarget { @@ -33,6 +32,32 @@ export const enum State { export type Edge = [State, number, State]; +export class Uint8Matrix { + + private readonly _data: Uint8Array; + public readonly rows: number; + public readonly cols: number; + + constructor(rows: number, cols: number, defaultValue: number) { + const data = new Uint8Array(rows * cols); + for (let i = 0, len = rows * cols; i < len; i++) { + data[i] = defaultValue; + } + + this._data = data; + this.rows = rows; + this.cols = cols; + } + + public get(row: number, col: number): number { + return this._data[row * this.cols + col]; + } + + public set(row: number, col: number, value: number): void { + this._data[row * this.cols + col] = value; + } +} + export class StateMachine { private readonly _states: Uint8Matrix; diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts index fbdda5d65a4..ddc03651456 100644 --- a/src/vs/editor/common/modes/modesRegistry.ts +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -60,5 +60,8 @@ LanguageConfigurationRegistry.register(PLAINTEXT_LANGUAGE_IDENTIFIER, { ['(', ')'], ['[', ']'], ['{', '}'], - ] + ], + folding: { + offSide: true + } }); diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts index af5a682bc37..e98f2b37f19 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -24,7 +24,7 @@ export function tokenizeToString(text: string, tokenizationSupport: IReducedToke return _tokenizeToString(text, tokenizationSupport || fallback); } -export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens, colorMap: string[], startOffset: number, endOffset: number, tabSize: number): string { +export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens, colorMap: string[], startOffset: number, endOffset: number, tabSize: number, useNbsp: boolean): string { let result = `
`; let charIndex = startOffset; let tabsCharDelta = 0; @@ -46,7 +46,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; tabsCharDelta += insertSpacesCount - 1; while (insertSpacesCount > 0) { - partContent += ' '; + partContent += useNbsp ? ' ' : ' '; insertSpacesCount--; } break; @@ -78,7 +78,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens break; case CharCode.Space: - partContent += ' '; + partContent += useNbsp ? ' ' : ' '; break; default: diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index fc3423047fb..d1492e39107 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -7,7 +7,6 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes'; -import { withUndefinedAsNull } from 'vs/base/common/types'; import { keys } from 'vs/base/common/map'; export class TokenizationRegistryImpl implements ITokenizationRegistry { @@ -18,11 +17,7 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { private readonly _onDidChange = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; - private _colorMap: Color[] | null; - - constructor() { - this._colorMap = null; - } + private _colorMap: Color[] | undefined; public fire(languages: string[]): void { this._onDidChange.fire({ @@ -76,8 +71,8 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { return null; } - public get(language: string): ITokenizationSupport | null { - return withUndefinedAsNull(this._map.get(language)); + public get(language: string): ITokenizationSupport | undefined { + return this._map.get(language); } public setColorMap(colorMap: Color[]): void { @@ -88,14 +83,14 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { }); } - public getColorMap(): Color[] | null { + public getColorMap(): Color[] | undefined { return this._colorMap; } - public getDefaultBackground(): Color | null { + public getDefaultBackground(): Color | undefined { if (this._colorMap && this._colorMap.length > ColorId.DefaultBackground) { return this._colorMap[ColorId.DefaultBackground]; } - return null; + return undefined; } } diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 1c9d7a274a9..5b5070184ea 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -218,6 +218,10 @@ export class ModelServiceImpl extends Disposable implements IModelService { } private static _setModelOptionsForModel(model: ITextModel, newOptions: ITextModelCreationOptions, currentOptions: ITextModelCreationOptions): void { + if (currentOptions && currentOptions.defaultEOL !== newOptions.defaultEOL && model.getLineCount() === 1) { + model.setEOL(newOptions.defaultEOL === DefaultEndOfLine.LF ? EndOfLineSequence.LF : EndOfLineSequence.CRLF); + } + if (currentOptions && (currentOptions.detectIndentation === newOptions.detectIndentation) && (currentOptions.insertSpaces === newOptions.insertSpaces) diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 5e79341a942..a384c18a977 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from 'vs/base/common/strings'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; +import { equals } from 'vs/base/common/arrays'; export class LineDecoration { _lineDecorationBrand: void; @@ -27,18 +28,8 @@ export class LineDecoration { ); } - public static equalsArr(a: LineDecoration[], b: LineDecoration[]): boolean { - let aLen = a.length; - let bLen = b.length; - if (aLen !== bLen) { - return false; - } - for (let i = 0; i < aLen; i++) { - if (!LineDecoration._equals(a[i], b[i])) { - return false; - } - } - return true; + public static equalsArr(a: readonly LineDecoration[], b: readonly LineDecoration[]): boolean { + return equals(a, b, LineDecoration._equals); } public static filter(lineDecorations: InlineDecoration[], lineNumber: number, minLineColumn: number, maxLineColumn: number): LineDecoration[] { diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index bbb9b39cb8c..0b41b006f65 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -9,6 +9,7 @@ import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; +import { equals } from 'vs/base/common/arrays'; export const enum RenderWhitespace { None = 0, @@ -131,17 +132,7 @@ export class RenderLineInput { return false; } - if (otherSelections.length !== this.selectionsOnLine.length) { - return false; - } - - for (let i = 0; i < this.selectionsOnLine.length; i++) { - if (!this.selectionsOnLine[i].equals(otherSelections[i])) { - return false; - } - } - - return true; + return equals(this.selectionsOnLine, otherSelections, (a, b) => a.equals(b)); } public equals(other: RenderLineInput): boolean { diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 74b236d4b78..bbf71e817b9 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -7,7 +7,7 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { toUint32Array } from 'vs/editor/common/core/uint'; +import { toUint32Array } from 'vs/base/common/uint'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection'; diff --git a/src/vs/editor/common/viewModel/prefixSumComputer.ts b/src/vs/editor/common/viewModel/prefixSumComputer.ts index 81e66ac5059..d4e8c1f0df2 100644 --- a/src/vs/editor/common/viewModel/prefixSumComputer.ts +++ b/src/vs/editor/common/viewModel/prefixSumComputer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toUint32 } from 'vs/editor/common/core/uint'; +import { toUint32 } from 'vs/base/common/uint'; export class PrefixSumIndexOfResult { _prefixSumIndexOfResultBrand: void; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 01e9f36723c..544a115ecf1 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -24,6 +24,7 @@ import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLi import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { ITheme } from 'vs/platform/theme/common/themeService'; import { RunOnceScheduler } from 'vs/base/common/async'; +import * as platform from 'vs/base/common/platform'; const USE_IDENTITY_LINES_COLLECTION = true; @@ -128,6 +129,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel super.dispose(); this.decorations.dispose(); this.lines.dispose(); + this.invalidateMinimapColorCache(); this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); } @@ -713,7 +715,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel if (lineContent === '') { result += '
'; } else { - result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize); + result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize, platform.isWindows); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 72af0135934..07f52a6ed51 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -5,6 +5,7 @@ import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Lazy } from 'vs/base/common/lazy'; import { Disposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -47,7 +48,7 @@ export class QuickFixController extends Disposable implements IEditorContributio private readonly _editor: ICodeEditor; private readonly _model: CodeActionModel; - private readonly _ui: CodeActionUi; + private readonly _ui: Lazy; constructor( editor: ICodeEditor, @@ -64,27 +65,29 @@ export class QuickFixController extends Disposable implements IEditorContributio this._editor = editor; this._model = this._register(new CodeActionModel(this._editor, markerService, contextKeyService, progressService)); - this._register(this._model.onDidChangeState((newState) => this.update(newState))); + this._register(this._model.onDidChangeState(newState => this.update(newState))); - this._ui = this._register(new CodeActionUi(editor, QuickFixAction.Id, { - applyCodeAction: async (action, retrigger) => { - try { - await this._applyCodeAction(action); - } finally { - if (retrigger) { - this._trigger({ type: 'auto', filter: {} }); + this._ui = new Lazy(() => + this._register(new CodeActionUi(editor, QuickFixAction.Id, { + applyCodeAction: async (action, retrigger) => { + try { + await this._applyCodeAction(action); + } finally { + if (retrigger) { + this._trigger({ type: 'auto', filter: {} }); + } } } - } - }, contextMenuService, keybindingService)); + }, contextMenuService, keybindingService)) + ); } private update(newState: CodeActionsState.State): void { - this._ui.update(newState); + this._ui.getValue().update(newState); } public showCodeActions(actions: CodeActionSet, at: IAnchor | IPosition) { - return this._ui.showCodeActionList(actions, at); + return this._ui.getValue().showCodeActionList(actions, at); } public getId(): string { diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index a496f3e708f..658aebe8e3a 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -17,11 +17,12 @@ import { CodeActionWidget } from './codeActionWidget'; import { LightBulbWidget } from './lightBulbWidget'; import { IPosition } from 'vs/editor/common/core/position'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; +import { Lazy } from 'vs/base/common/lazy'; export class CodeActionUi extends Disposable { - private readonly _codeActionWidget: CodeActionWidget; - private readonly _lightBulbWidget: LightBulbWidget; + private readonly _codeActionWidget: Lazy; + private readonly _lightBulbWidget: Lazy; private readonly _activeCodeActions = this._register(new MutableDisposable()); constructor( @@ -35,19 +36,26 @@ export class CodeActionUi extends Disposable { ) { super(); - this._codeActionWidget = this._register(new CodeActionWidget(this._editor, contextMenuService, { - onSelectCodeAction: async (action) => { - this.delegate.applyCodeAction(action, /* retrigger */ true); - } - })); - this._lightBulbWidget = this._register(new LightBulbWidget(this._editor, quickFixActionId, keybindingService)); + this._codeActionWidget = new Lazy(() => { + return this._register(new CodeActionWidget(this._editor, contextMenuService, { + onSelectCodeAction: async (action) => { + this.delegate.applyCodeAction(action, /* retrigger */ true); + } + })); + }); - this._register(this._lightBulbWidget.onClick(this._handleLightBulbSelect, this)); + this._lightBulbWidget = new Lazy(() => { + const widget = this._register(new LightBulbWidget(this._editor, quickFixActionId, keybindingService)); + this._register(widget.onClick(this._handleLightBulbSelect, this)); + return widget; + }); } public async update(newState: CodeActionsState.State): Promise { if (newState.type !== CodeActionsState.Type.Triggered) { - this._lightBulbWidget.hide(); + if (this._lightBulbWidget.hasValue()) { + this._lightBulbWidget.getValue().hide(); + } return; } @@ -59,7 +67,7 @@ export class CodeActionUi extends Disposable { return; } - this._lightBulbWidget.update(actions, newState.position); + this._lightBulbWidget.getValue().update(actions, newState.position); if (!actions.actions.length && newState.trigger.context) { MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); @@ -83,10 +91,10 @@ export class CodeActionUi extends Disposable { } } this._activeCodeActions.value = actions; - this._codeActionWidget.show(actions, newState.position); + this._codeActionWidget.getValue().show(actions, newState.position); } else { // auto magically triggered - if (this._codeActionWidget.isVisible) { + if (this._codeActionWidget.getValue().isVisible) { // TODO: Figure out if we should update the showing menu? actions.dispose(); } else { @@ -96,10 +104,10 @@ export class CodeActionUi extends Disposable { } public async showCodeActionList(actions: CodeActionSet, at?: IAnchor | IPosition): Promise { - this._codeActionWidget.show(actions, at); + this._codeActionWidget.getValue().show(actions, at); } private _handleLightBulbSelect(e: { x: number, y: number, actions: CodeActionSet }): void { - this._codeActionWidget.show(e.actions, e); + this._codeActionWidget.getValue().show(e.actions, e); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index 4f31015c81c..cb5b1eb4b2b 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -21,7 +21,7 @@ interface CodeActionWidgetDelegate { export class CodeActionWidget extends Disposable { - private _visible: boolean; + private _visible: boolean = false; private readonly _showingActions = this._register(new MutableDisposable()); constructor( @@ -30,7 +30,6 @@ export class CodeActionWidget extends Disposable { private readonly _delegate: CodeActionWidgetDelegate, ) { super(); - this._visible = false; } public async show(codeActions: CodeActionSet, at?: IAnchor | IPosition): Promise { diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.css b/src/vs/editor/contrib/codeAction/lightBulbWidget.css index afacdc3d2ec..aadcb4fd6e6 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.css +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.css @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.monaco-editor .lightbulb-glyph, .monaco-editor .codicon-lightbulb { display: flex; align-items: center; @@ -12,6 +13,7 @@ padding-left: 2px; } +.monaco-editor .lightbulb-glyph:hover, .monaco-editor .codicon-lightbulb:hover { cursor: pointer; /* transform: scale(1.3, 1.3); */ diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 42cf0deb06e..5465476d6ed 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -6,7 +6,7 @@ import 'vs/css!./codelensWidget'; import * as dom from 'vs/base/browser/dom'; import { coalesce, isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { escape } from 'vs/base/common/strings'; +import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; @@ -104,7 +104,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { for (let i = 0; i < symbols.length; i++) { const command = symbols[i].command; if (command) { - const title = escape(command.title); + const title = renderOcticons(command.title); let part: string; if (command.id) { part = `${title}`; diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index c1460b7650d..7e2b9e03de4 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -27,7 +27,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { private static readonly ID: string = 'editor.contrib.colorDetector'; - static RECOMPUTE_TIME = 1000; // ms + static readonly RECOMPUTE_TIME = 1000; // ms private readonly _localToDispose = this._register(new DisposableStore()); private _computePromise: CancelablePromise | null; diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 64329a79ba9..4a74a4a5e0d 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -32,7 +32,7 @@ export class ColorPickerHeader extends Disposable { this.pickedColorNode = dom.append(this.domNode, $('.picked-color')); const colorBox = dom.append(this.domNode, $('.original-color')); - colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor); + colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor) || ''; this.backgroundColor = themeService.getTheme().getColor(editorHoverBackground) || Color.white; this._register(registerThemingParticipant((theme, collector) => { @@ -46,12 +46,12 @@ export class ColorPickerHeader extends Disposable { })); this._register(model.onDidChangeColor(this.onDidChangeColor, this)); this._register(model.onDidChangePresentation(this.onDidChangePresentation, this)); - this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(model.color); + this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(model.color) || ''; dom.toggleClass(this.pickedColorNode, 'light', model.color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : model.color.isLighter()); } private onDidChangeColor(color: Color): void { - this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color); + this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color) || ''; dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter()); this.onDidChangePresentation(); } diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index a097d994648..5c3716ed79c 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -13,6 +13,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; +import { Constants } from 'vs/base/common/uint'; export interface IInsertionPoint { ignore: boolean; @@ -392,7 +393,7 @@ export class LineCommentCommand implements editorCommon.ICommand { * Adjust insertion points to have them vertically aligned in the add line comment case */ public static _normalizeInsertionPoint(model: ISimpleModel, lines: IInsertionPoint[], startLineNumber: number, tabSize: number): void { - let minVisibleColumn = Number.MAX_VALUE; + let minVisibleColumn = Constants.MAX_SAFE_SMALL_INTEGER; let j: number; let lenJ: number; diff --git a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts index 0d9ea4ea21e..e4568052f62 100644 --- a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts +++ b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts @@ -12,6 +12,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { equals } from 'vs/base/common/arrays'; class CursorState { readonly selections: readonly Selection[]; @@ -21,17 +22,7 @@ class CursorState { } public equals(other: CursorState): boolean { - const thisLen = this.selections.length; - const otherLen = other.selections.length; - if (thisLen !== otherLen) { - return false; - } - for (let i = 0; i < thisLen; i++) { - if (!this.selections[i].equalsSelection(other.selections[i])) { - return false; - } - } - return true; + return equals(this.selections, other.selections, (a, b) => a.equalsSelection(b)); } } diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index bbf3df37d01..044ea027dc3 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -38,7 +38,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE private _dndDecorationIds: string[]; private _mouseDown: boolean; private _modifierPressed: boolean; - static TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl; + static readonly TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl; static get(editor: ICodeEditor): DragAndDropController { return editor.getContribution(DragAndDropController.ID); diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 84ac8bb704c..eb5fd2760fb 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -6,13 +6,13 @@ import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter, ITreeFilter } from 'vs/base/browser/ui/tree/tree'; import { values } from 'vs/base/common/collections'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import 'vs/css!./media/outlineTree'; import 'vs/css!./media/symbol-icons'; import { Range } from 'vs/editor/common/core/range'; -import { SymbolKind, symbolKindToCssClass, SymbolTag } from 'vs/editor/common/modes'; +import { SymbolKind, SymbolKinds, SymbolTag } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -44,7 +44,7 @@ export class OutlineIdentityProvider implements IIdentityProvider { } export class OutlineGroupTemplate { - static id = 'OutlineGroupTemplate'; + static readonly id = 'OutlineGroupTemplate'; constructor( readonly labelContainer: HTMLElement, readonly label: HighlightedLabel, @@ -52,7 +52,7 @@ export class OutlineGroupTemplate { } export class OutlineElementTemplate { - static id = 'OutlineElementTemplate'; + static readonly id = 'OutlineElementTemplate'; constructor( readonly container: HTMLElement, readonly iconLabel: IconLabel, @@ -125,7 +125,7 @@ export class OutlineElementRenderer implements ITreeRenderer= 0) { options.extraClasses.push(`deprecated`); @@ -214,6 +214,31 @@ export const enum OutlineSortOrder { ByKind } +export class OutlineFilter implements ITreeFilter { + + private readonly _filteredTypes = new Set(); + + constructor( + private readonly _prefix: string, + @IConfigurationService private readonly _configService: IConfigurationService, + ) { + + } + + update() { + this._filteredTypes.clear(); + for (const name of SymbolKinds.names()) { + if (!this._configService.getValue(`${this._prefix}.${name}`)) { + this._filteredTypes.add(SymbolKinds.fromString(name) || -1); + } + } + } + + filter(element: OutlineItem): boolean { + return !(element instanceof OutlineElement) || !this._filteredTypes.has(element.symbol.kind); + } +} + export class OutlineItemComparator implements ITreeSorter { private readonly _collator = new IdleValue(() => new Intl.Collator(undefined, { numeric: true })); diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index e039801431f..da67f86eb78 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -12,7 +12,7 @@ import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/commo import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, FindMatch, ITextModel } from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; diff --git a/src/vs/editor/contrib/find/test/replacePattern.test.ts b/src/vs/editor/contrib/find/test/replacePattern.test.ts index 3371aa02994..2ed4a5c3056 100644 --- a/src/vs/editor/contrib/find/test/replacePattern.test.ts +++ b/src/vs/editor/contrib/find/test/replacePattern.test.ts @@ -183,6 +183,10 @@ suite('Replace Pattern test', () => { assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar-newabc'), 'Newfoo-Newbar-Newabc'); actual = ['Foo-Bar-abc']; assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'Newfoo-newbar'); + actual = ['foo-Bar']; + assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'newfoo-Newbar'); + actual = ['foo-BAR']; + assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'newfoo-NEWBAR'); actual = ['Foo_Bar']; assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'Newfoo_Newbar'); @@ -192,6 +196,10 @@ suite('Replace Pattern test', () => { assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'Newfoo_newbar'); actual = ['Foo_Bar-abc']; assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar-abc'), 'Newfoo_newbar-abc'); + actual = ['foo_Bar']; + assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'newfoo_Newbar'); + actual = ['Foo_BAR']; + assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo_newbar'), 'Newfoo_NEWBAR'); }); test('preserve case', () => { @@ -227,6 +235,14 @@ suite('Replace Pattern test', () => { actual = replacePattern.buildReplaceString(['Foo-Bar-abc'], true); assert.equal(actual, 'Newfoo-newbar'); + replacePattern = parseReplaceString('newfoo-newbar'); + actual = replacePattern.buildReplaceString(['foo-Bar'], true); + assert.equal(actual, 'newfoo-Newbar'); + + replacePattern = parseReplaceString('newfoo-newbar'); + actual = replacePattern.buildReplaceString(['foo-BAR'], true); + assert.equal(actual, 'newfoo-NEWBAR'); + replacePattern = parseReplaceString('newfoo_newbar'); actual = replacePattern.buildReplaceString(['Foo_Bar'], true); assert.equal(actual, 'Newfoo_Newbar'); @@ -242,5 +258,13 @@ suite('Replace Pattern test', () => { replacePattern = parseReplaceString('newfoo_newbar-abc'); actual = replacePattern.buildReplaceString(['Foo_Bar-abc'], true); assert.equal(actual, 'Newfoo_newbar-abc'); + + replacePattern = parseReplaceString('newfoo_newbar'); + actual = replacePattern.buildReplaceString(['foo_Bar'], true); + assert.equal(actual, 'newfoo_Newbar'); + + replacePattern = parseReplaceString('newfoo_newbar'); + actual = replacePattern.buildReplaceString(['foo_BAR'], true); + assert.equal(actual, 'newfoo_NEWBAR'); }); }); diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index f0ae51e1955..87fa92fb892 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -14,7 +14,7 @@ import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType } from 'vs/editor/contrib/folding/foldingModel'; +import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, toggleCollapseState } from 'vs/editor/contrib/folding/foldingModel'; import { FoldingDecorationProvider } from './foldingDecorations'; import { FoldingRegions, FoldingRegion } from './foldingRanges'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -50,7 +50,7 @@ interface FoldingStateMemento { export class FoldingController extends Disposable implements IEditorContribution { - static MAX_FOLDING_REGIONS = 5000; + static readonly MAX_FOLDING_REGIONS = 5000; public static get(editor: ICodeEditor): FoldingController { @@ -656,6 +656,30 @@ class FoldAction extends FoldingAction { } } + +class ToggleFoldAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.toggleFold', + label: nls.localize('toggleFoldAction.label', "Toggle Fold"), + alias: 'Toggle Fold', + precondition: CONTEXT_FOLDING_ENABLED, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_L), + weight: KeybindingWeight.EditorContrib + } + }); + } + + invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void { + let selectedLines = this.getSelectedLines(editor); + toggleCollapseState(foldingModel, 1, selectedLines); + } +} + + class FoldRecursivelyAction extends FoldingAction { constructor() { @@ -842,6 +866,7 @@ registerEditorAction(UnfoldAllAction); registerEditorAction(FoldAllBlockCommentsAction); registerEditorAction(FoldAllRegionsAction); registerEditorAction(UnfoldAllRegionsAction); +registerEditorAction(ToggleFoldAction); for (let i = 1; i <= 7; i++) { registerInstantiatedEditorAction( diff --git a/src/vs/editor/contrib/folding/foldingDecorations.ts b/src/vs/editor/contrib/folding/foldingDecorations.ts index 44554ac1300..333ad459db0 100644 --- a/src/vs/editor/contrib/folding/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/foldingDecorations.ts @@ -10,18 +10,18 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; export class FoldingDecorationProvider implements IDecorationProvider { - private static COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ + private static readonly COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, afterContentClassName: 'inline-folded', linesDecorationsClassName: 'codicon codicon-chevron-right' }); - private static EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({ + private static readonly EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, linesDecorationsClassName: 'codicon codicon-chevron-down' }); - private static EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ + private static readonly EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, linesDecorationsClassName: 'codicon codicon-chevron-down alwaysShowFoldIcons' }); diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index 15cc62d2098..44feb79cdf2 100644 --- a/src/vs/editor/contrib/folding/foldingModel.ts +++ b/src/vs/editor/contrib/folding/foldingModel.ts @@ -242,6 +242,26 @@ export class FoldingModel { } +/** + * Collapse or expand the regions at the given locations + * @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels. + * @param lineNumbers the location of the regions to collapse or expand, or if not set, all regions in the model. + */ +export function toggleCollapseState(foldingModel: FoldingModel, levels: number, lineNumbers: number[]) { + let toToggle: FoldingRegion[] = []; + for (let lineNumber of lineNumbers) { + let region = foldingModel.getRegionAtLine(lineNumber); + if (region) { + const doCollapse = !region.isCollapsed; + toToggle.push(region); + if (levels > 1) { + let regionsInside = foldingModel.getRegionsInside(region, (r, level: number) => r.isCollapsed !== doCollapse && level < levels); + toToggle.push(...regionsInside); + } + } + } + foldingModel.toggleCollapseState(toToggle); +} /** diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 1844a9f3456..e234b8d299d 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -31,6 +31,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { EditorStateCancellationTokenSource, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; import { ISymbolNavigationService } from 'vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { isEqual } from 'vs/base/common/resources'; export class DefinitionActionConfig { @@ -84,7 +85,7 @@ export class DefinitionAction extends EditorAction { } const newLen = result.push(reference); if (this._configuration.filterCurrent - && reference.uri.toString() === model.uri.toString() + && isEqual(reference.uri, model.uri) && Range.containsPosition(reference.range, pos) && idxOfCurrent === -1 ) { diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 7d0dd7ce4e1..589e552e225 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -30,7 +30,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.gotodefinitionwithmouse'; - static MAX_SOURCE_PREVIEW_LINES = 8; + static readonly MAX_SOURCE_PREVIEW_LINES = 8; private readonly editor: ICodeEditor; private readonly toUnhook = new DisposableStore(); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts index e52bc043383..bbf2cddd212 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts @@ -18,6 +18,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { isEqual } from 'vs/base/common/resources'; export const ctxHasSymbols = new RawContextKey('hasSymbols', false); @@ -92,7 +93,7 @@ class SymbolNavigationService implements ISymbolNavigationService { let seenUri: boolean = false; let seenPosition: boolean = false; for (const reference of refModel.references) { - if (reference.uri.toString() === model.uri.toString()) { + if (isEqual(reference.uri, model.uri)) { seenUri = true; seenPosition = seenPosition || Range.containsPosition(reference.range, position); } else if (seenUri) { diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index c16b9be9a6e..291b30cbb25 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -20,12 +20,13 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { MarkerNavigationWidget } from './gotoErrorWidget'; import { compare } from 'vs/base/common/strings'; -import { binarySearch } from 'vs/base/common/arrays'; +import { binarySearch, find } from 'vs/base/common/arrays'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { onUnexpectedError } from 'vs/base/common/errors'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { isEqual } from 'vs/base/common/resources'; class MarkerModel { @@ -172,12 +173,7 @@ class MarkerModel { } public findMarkerAtPosition(pos: Position): IMarker | undefined { - for (const marker of this._markers) { - if (Range.containsPosition(marker, pos)) { - return marker; - } - } - return undefined; + return find(this._markers, marker => Range.containsPosition(marker, pos)); } public get total() { @@ -248,8 +244,8 @@ export class MarkerController implements editorCommon.IEditorContribution { const prevMarkerKeybinding = this._keybindingService.lookupKeybinding(PrevMarkerAction.ID); const nextMarkerKeybinding = this._keybindingService.lookupKeybinding(NextMarkerAction.ID); const actions = [ - new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } }), - new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } }) + new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem codicon-chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } }), + new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem codicon-chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } }) ]; this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService); this._widgetVisible.set(true); @@ -310,7 +306,7 @@ export class MarkerController implements editorCommon.IEditorContribution { } private _onMarkerChanged(changedResources: URI[]): void { - let editorModel = this._editor.getModel(); + const editorModel = this._editor.getModel(); if (!editorModel) { return; } @@ -319,7 +315,7 @@ export class MarkerController implements editorCommon.IEditorContribution { return; } - if (!changedResources.some(r => editorModel!.uri.toString() === r.toString())) { + if (!changedResources.some(r => isEqual(editorModel.uri, r))) { return; } this._model.setMarkers(this._getMarkers()); @@ -371,7 +367,7 @@ class MarkerNavigationAction extends EditorAction { return Promise.resolve(undefined); } - let editorModel = editor.getModel(); + const editorModel = editor.getModel(); if (!editorModel) { return Promise.resolve(undefined); } @@ -389,7 +385,7 @@ class MarkerNavigationAction extends EditorAction { } let newMarker = markers[idx]; - if (newMarker.resource.toString() === editorModel!.uri.toString()) { + if (isEqual(newMarker.resource, editorModel.uri)) { // the next `resource` is this resource which // means we cycle within this file model.move(this._isNext, true); @@ -522,4 +518,4 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem") }, order: 2 -}); \ No newline at end of file +}); diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 3c94a3b561a..a8aa26cb3a9 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -283,7 +283,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { : nls.localize('change', "{0} of {1} problem", markerIdx, markerCount); this.setTitle(basename(model.uri), detail); } - this._icon.className = SeverityIcon.className(MarkerSeverity.toSeverity(this._severity)); + this._icon.className = `codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(this._severity))}`; this.editor.revealPositionInCenter(position, ScrollType.Smooth); } diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/editor/contrib/hover/hover.css index 8190705b9a5..49a1a6b0f4f 100644 --- a/src/vs/editor/contrib/hover/hover.css +++ b/src/vs/editor/contrib/hover/hover.css @@ -37,7 +37,7 @@ margin: 8px 0; } -.monaco-editor-hover p > code { +.monaco-editor-hover code { font-family: var(--monaco-monospace-font); } diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 215ddd5844c..ac7d39b6d94 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -7,7 +7,7 @@ import 'vs/css!./hover'; import * as nls from 'vs/nls'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -34,21 +34,21 @@ export class ModesHoverController implements IEditorContribution { private readonly _toUnhook = new DisposableStore(); private readonly _didChangeConfigurationHandler: IDisposable; - private _contentWidget: ModesContentHoverWidget | null; - private _glyphWidget: ModesGlyphHoverWidget | null; + private readonly _contentWidget = new MutableDisposable(); + private readonly _glyphWidget = new MutableDisposable(); get contentWidget(): ModesContentHoverWidget { - if (!this._contentWidget) { + if (!this._contentWidget.value) { this._createHoverWidgets(); } - return this._contentWidget!; + return this._contentWidget.value!; } get glyphWidget(): ModesGlyphHoverWidget { - if (!this._glyphWidget) { + if (!this._glyphWidget.value) { this._createHoverWidgets(); } - return this._glyphWidget!; + return this._glyphWidget.value!; } private _isMouseDown: boolean; @@ -69,8 +69,6 @@ export class ModesHoverController implements IEditorContribution { ) { this._isMouseDown = false; this._hoverClicked = false; - this._contentWidget = null; - this._glyphWidget = null; this._hookEvents(); @@ -197,17 +195,17 @@ export class ModesHoverController implements IEditorContribution { } private _hideWidgets(): void { - if (!this._glyphWidget || !this._contentWidget || (this._isMouseDown && this._hoverClicked && this._contentWidget.isColorPickerVisible())) { + if (!this._glyphWidget.value || !this._contentWidget.value || (this._isMouseDown && this._hoverClicked && this._contentWidget.value.isColorPickerVisible())) { return; } - this._glyphWidget!.hide(); - this._contentWidget.hide(); + this._glyphWidget.value.hide(); + this._contentWidget.value.hide(); } private _createHoverWidgets() { - this._contentWidget = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService); - this._glyphWidget = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService); + this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService); + this._glyphWidget.value = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService); } public showContentHover(range: Range, mode: HoverStartMode, focus: boolean): void { @@ -222,13 +220,8 @@ export class ModesHoverController implements IEditorContribution { this._unhookEvents(); this._toUnhook.dispose(); this._didChangeConfigurationHandler.dispose(); - - if (this._glyphWidget) { - this._glyphWidget.dispose(); - } - if (this._contentWidget) { - this._contentWidget.dispose(); - } + this._glyphWidget.dispose(); + this._contentWidget.dispose(); } } diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 06877b671d5..8dae61e55d0 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -38,6 +38,7 @@ import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Constants } from 'vs/base/common/uint'; const $ = dom.$; @@ -333,7 +334,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._colorPicker = null; // update column from which to show - let renderColumn = Number.MAX_VALUE; + let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; let highlightRange: Range | null = messages[0].range ? Range.lift(messages[0].range) : null; let fragment = document.createDocumentFragment(); let isEmptyHoverContent = true; diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 1eee5898a96..184fa18ecf0 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -44,8 +44,7 @@ function getHoverMessage(link: Link, useMetaKey: boolean): MarkdownString { : nls.localize('links.navigate.kb.alt', "alt + click"); if (link.url) { - const hoverMessage = new MarkdownString().appendMarkdown(`[${label}](${link.url.toString()}) (${kb})`); - hoverMessage.isTrusted = true; + const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString()}) (${kb})`); return hoverMessage; } else { return new MarkdownString().appendText(`${label} (${kb})`); @@ -106,7 +105,7 @@ class LinkDetector implements editorCommon.IEditorContribution { return editor.getContribution(LinkDetector.ID); } - static RECOMPUTE_TIME = 1000; // ms + static readonly RECOMPUTE_TIME = 1000; // ms private readonly editor: ICodeEditor; private enabled: boolean; diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index e57a0987498..311a922e7da 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -23,7 +23,7 @@ export class MessageController extends Disposable implements editorCommon.IEdito private static readonly _id = 'editor.contrib.messageController'; - static MESSAGE_VISIBLE = new RawContextKey('messageVisible', false); + static readonly MESSAGE_VISIBLE = new RawContextKey('messageVisible', false); static get(editor: ICodeEditor): MessageController { return editor.getContribution(MessageController._id); diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index a76a1d62e96..11516c57f83 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -14,7 +14,7 @@ import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/comm import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { FindMatch, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 2b1b9c8fbfa..a6040059de6 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -34,13 +34,17 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, private readonly model = this._register(new MutableDisposable()); private readonly keyVisible: IContextKey; private readonly keyMultipleSignatures: IContextKey; - private element: HTMLElement; - private signature: HTMLElement; - private docs: HTMLElement; - private overloads: HTMLElement; - private visible: boolean; - private announcedLabel: string | null; - private scrollbar: DomScrollableElement; + + private domNodes?: { + readonly element: HTMLElement; + readonly signature: HTMLElement; + readonly docs: HTMLElement; + readonly overloads: HTMLElement; + readonly scrollbar: DomScrollableElement; + }; + + private visible: boolean = false; + private announcedLabel: string | null = null; // Editor.IContentWidget.allowEditorOverflow allowEditorOverflow = true; @@ -56,7 +60,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, this.model.value = new ParameterHintsModel(editor); this.keyVisible = Context.Visible.bindTo(contextKeyService); this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService); - this.visible = false; this._register(this.model.value.onChangedHints(newParameterHints => { if (newParameterHints) { @@ -69,8 +72,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } private createParamaterHintDOMNodes() { - this.element = $('.editor-widget.parameter-hints-widget'); - const wrapper = dom.append(this.element, $('.wrapper')); + const element = $('.editor-widget.parameter-hints-widget'); + const wrapper = dom.append(element, $('.wrapper')); wrapper.tabIndex = -1; const buttons = dom.append(wrapper, $('.buttons')); @@ -83,21 +86,29 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, const onNextClick = stop(domEvent(next, 'click')); this._register(onNextClick(this.next, this)); - this.overloads = dom.append(wrapper, $('.overloads')); + const overloads = dom.append(wrapper, $('.overloads')); const body = $('.body'); - this.scrollbar = new DomScrollableElement(body, {}); - this._register(this.scrollbar); - wrapper.appendChild(this.scrollbar.getDomNode()); + const scrollbar = new DomScrollableElement(body, {}); + this._register(scrollbar); + wrapper.appendChild(scrollbar.getDomNode()); - this.signature = dom.append(body, $('.signature')); + const signature = dom.append(body, $('.signature')); + const docs = dom.append(body, $('.docs')); - this.docs = dom.append(body, $('.docs')); + element.style.userSelect = 'text'; + + this.domNodes = { + element, + signature, + overloads, + docs, + scrollbar, + }; this.editor.addContentWidget(this); this.hide(); - this.element.style.userSelect = 'text'; this._register(this.editor.onDidChangeCursorSelection(e => { if (this.visible) { this.editor.layoutContentWidget(this); @@ -105,8 +116,11 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, })); const updateFont = () => { + if (!this.domNodes) { + return; + } const fontInfo = this.editor.getOption(EditorOption.fontInfo); - this.element.style.fontSize = `${fontInfo.fontSize}px`; + this.domNodes.element.style.fontSize = `${fontInfo.fontSize}px`; }; updateFont(); @@ -124,13 +138,17 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, return; } - if (!this.element) { + if (!this.domNodes) { this.createParamaterHintDOMNodes(); } this.keyVisible.set(true); this.visible = true; - setTimeout(() => dom.addClass(this.element, 'visible'), 100); + setTimeout(() => { + if (this.domNodes) { + dom.addClass(this.domNodes.element, 'visible'); + } + }, 100); this.editor.layoutContentWidget(this); } @@ -139,14 +157,12 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, return; } - if (!this.element) { - this.createParamaterHintDOMNodes(); - } - this.keyVisible.reset(); this.visible = false; this.announcedLabel = null; - dom.removeClass(this.element, 'visible'); + if (this.domNodes) { + dom.removeClass(this.domNodes.element, 'visible'); + } this.editor.layoutContentWidget(this); } @@ -161,12 +177,16 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } private render(hints: modes.SignatureHelp): void { + if (!this.domNodes) { + return; + } + const multiple = hints.signatures.length > 1; - dom.toggleClass(this.element, 'multiple', multiple); + dom.toggleClass(this.domNodes.element, 'multiple', multiple); this.keyMultipleSignatures.set(multiple); - this.signature.innerHTML = ''; - this.docs.innerHTML = ''; + this.domNodes.signature.innerHTML = ''; + this.domNodes.docs.innerHTML = ''; const signature = hints.signatures[hints.activeSignature]; @@ -174,7 +194,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, return; } - const code = dom.append(this.signature, $('.code')); + const code = dom.append(this.domNodes.signature, $('.code')); const hasParameters = signature.parameters.length > 0; const fontInfo = this.editor.getOption(EditorOption.fontInfo); @@ -203,17 +223,17 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, this.renderDisposeables.add(renderedContents); documentation.appendChild(renderedContents.element); } - dom.append(this.docs, $('p', {}, documentation)); + dom.append(this.domNodes.docs, $('p', {}, documentation)); } if (signature.documentation === undefined) { /** no op */ } else if (typeof signature.documentation === 'string') { - dom.append(this.docs, $('p', {}, signature.documentation)); + dom.append(this.domNodes.docs, $('p', {}, signature.documentation)); } else { const renderedContents = this.markdownRenderer.render(signature.documentation); dom.addClass(renderedContents.element, 'markdown-docs'); this.renderDisposeables.add(renderedContents); - dom.append(this.docs, renderedContents.element); + dom.append(this.domNodes.docs, renderedContents.element); } let hasDocs = false; @@ -230,8 +250,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, hasDocs = true; } - dom.toggleClass(this.signature, 'has-docs', hasDocs); - dom.toggleClass(this.docs, 'empty', !hasDocs); + dom.toggleClass(this.domNodes.signature, 'has-docs', hasDocs); + dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs); let currentOverload = String(hints.activeSignature + 1); @@ -239,7 +259,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, currentOverload += `/${hints.signatures.length}`; } - this.overloads.textContent = currentOverload; + this.domNodes.overloads.textContent = currentOverload; if (activeParameter) { const labelToAnnounce = this.getParameterLabel(signature, hints.activeParameter); @@ -253,7 +273,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } this.editor.layoutContentWidget(this); - this.scrollbar.scanDomNode(); + this.domNodes.scrollbar.scanDomNode(); } private renderParameters(parent: HTMLElement, signature: modes.SignatureInformation, currentParameter: number): void { @@ -317,7 +337,10 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } getDomNode(): HTMLElement { - return this.element; + if (!this.domNodes) { + this.createParamaterHintDOMNodes(); + } + return this.domNodes!.element; } getId(): string { @@ -331,10 +354,13 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } private updateMaxHeight(): void { + if (!this.domNodes) { + return; + } const height = Math.max(this.editor.getLayoutInfo().height / 4, 250); const maxHeight = `${height}px`; - this.element.style.maxHeight = maxHeight; - const wrapper = this.element.getElementsByClassName('wrapper') as HTMLCollectionOf; + this.domNodes.element.style.maxHeight = maxHeight; + const wrapper = this.domNodes.element.getElementsByClassName('wrapper') as HTMLCollectionOf; if (wrapper.length) { wrapper[0].style.maxHeight = maxHeight; } diff --git a/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css b/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css index b0d1fefcf21..091c6cd70e7 100644 --- a/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css +++ b/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css @@ -60,3 +60,7 @@ border-top: 1px solid; position: relative; } + +.monaco-editor .peekview-widget .head .peekview-title .codicon { + margin-right: 4px; +} diff --git a/src/vs/editor/contrib/referenceSearch/referencesModel.ts b/src/vs/editor/contrib/referenceSearch/referencesModel.ts index 36d54e60852..579625ed01d 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesModel.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesModel.ts @@ -15,6 +15,7 @@ import { Location, LocationLink } from 'vs/editor/common/modes'; import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { Position } from 'vs/editor/common/core/position'; import { IMatch } from 'vs/base/common/filters'; +import { Constants } from 'vs/base/common/uint'; export class OneReference { readonly id: string; @@ -72,7 +73,7 @@ export class FilePreview implements IDisposable { const { startLineNumber, startColumn, endLineNumber, endColumn } = range; const word = model.getWordUntilPosition({ lineNumber: startLineNumber, column: startColumn - n }); const beforeRange = new Range(startLineNumber, word.startColumn, startLineNumber, startColumn); - const afterRange = new Range(endLineNumber, endColumn, endLineNumber, Number.MAX_VALUE); + const afterRange = new Range(endLineNumber, endColumn, endLineNumber, Constants.MAX_SAFE_SMALL_INTEGER); const before = model.getValueInRange(beforeRange).replace(/^\s+/, ''); const inside = model.getValueInRange(range); diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 627902b09b9..7a5bae4a3db 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -10,7 +10,7 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, IReference, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; +import { basenameOrAuthority, dirname, isEqual } from 'vs/base/common/resources'; import 'vs/css!./media/referencesWidget'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; @@ -66,7 +66,7 @@ class DecorationsManager implements IDisposable { const model = this._editor.getModel(); if (model) { for (const ref of this._model.groups) { - if (ref.uri.toString() === model.uri.toString()) { + if (isEqual(ref.uri, model.uri)) { this._addDecorations(ref); return; } @@ -158,8 +158,8 @@ class DecorationsManager implements IDisposable { } export class LayoutData { - ratio: number = 0.7; - heightInLines: number = 18; + public ratio: number = 0.7; + public heightInLines: number = 18; static fromJSON(raw: string): LayoutData { let ratio: number | undefined; @@ -179,9 +179,9 @@ export class LayoutData { } export interface SelectionEvent { - kind: 'goto' | 'show' | 'side' | 'open'; - source: 'editor' | 'tree' | 'title'; - element?: Location; + readonly kind: 'goto' | 'show' | 'side' | 'open'; + readonly source: 'editor' | 'tree' | 'title'; + readonly element?: Location; } export const ctxReferenceWidgetSearchTreeFocused = new RawContextKey('referenceSearchTreeFocused', true); @@ -366,21 +366,6 @@ export class ReferenceWidget extends PeekViewWidget { this._tree.onDidChangeFocus(e => { onEvent(e.elements[0], 'show'); }); - this._tree.onDidChangeSelection(e => { - let aside = false; - let goto = false; - if (e.browserEvent instanceof KeyboardEvent) { - // todo@joh make this a command - goto = true; - } - if (aside) { - onEvent(e.elements[0], 'side'); - } else if (goto) { - onEvent(e.elements[0], 'goto'); - } else { - onEvent(e.elements[0], 'show'); - } - }); this._tree.onDidOpen(e => { const aside = (e.browserEvent instanceof MouseEvent) && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey); let goto = !e.browserEvent || ((e.browserEvent instanceof MouseEvent) && e.browserEvent.detail === 2); diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 8c0858b6984..796a028890b 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -20,15 +20,15 @@ import { Position, IPosition } from 'vs/editor/common/core/position'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; import { MessageController } from 'vs/editor/contrib/message/messageController'; -import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; +import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/browser/core/editorState'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IdleValue, raceCancellation } from 'vs/base/common/async'; import { withNullAsUndefined } from 'vs/base/common/types'; class RenameSkeleton { @@ -95,21 +95,17 @@ export async function rename(model: ITextModel, position: Position, newName: str // --- register actions and commands -class RenameController extends Disposable implements IEditorContribution { +class RenameController implements IEditorContribution { private static readonly ID = 'editor.contrib.renameController'; - public static get(editor: ICodeEditor): RenameController { + static get(editor: ICodeEditor): RenameController { return editor.getContribution(RenameController.ID); } - private _renameInputField?: RenameInputField; - private _renameOperationIdPool = 1; - - private _activeRename?: { - readonly id: number; - readonly operation: CancelablePromise; - }; + private readonly _renameInputField: IdleValue; + private readonly _dispoableStore = new DisposableStore(); + private _cts: CancellationTokenSource = new CancellationTokenSource(); constructor( private readonly editor: ICodeEditor, @@ -119,37 +115,22 @@ class RenameController extends Disposable implements IEditorContribution { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IThemeService private readonly _themeService: IThemeService, ) { - super(); - this._register(this.editor.onDidChangeModel(() => this.onModelChanged())); - this._register(this.editor.onDidChangeModelLanguage(() => this.onModelChanged())); - this._register(this.editor.onDidChangeCursorSelection(() => this.onModelChanged())); - } - - private get renameInputField(): RenameInputField { - if (!this._renameInputField) { - this._renameInputField = this._register(new RenameInputField(this.editor, this._themeService, this._contextKeyService)); - } - return this._renameInputField; + this._renameInputField = new IdleValue(() => this._dispoableStore.add(new RenameInputField(this.editor, this._themeService, this._contextKeyService))); } getId(): string { return RenameController.ID; } - async run(): Promise { - if (this._activeRename) { - this._activeRename.operation.cancel(); - } - - const id = this._renameOperationIdPool++; - this._activeRename = { - id, - operation: createCancelablePromise(token => this.doRename(token, id)) - }; - return this._activeRename.operation; + dispose(): void { + this._dispoableStore.dispose(); + this._cts.dispose(true); } - private async doRename(token: CancellationToken, id: number): Promise { + async run(): Promise { + + this._cts.dispose(true); + if (!this.editor.hasModel()) { return undefined; } @@ -161,9 +142,12 @@ class RenameController extends Disposable implements IEditorContribution { return undefined; } + this._cts = new EditorStateCancellationTokenSource(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value); + + // resolve rename location let loc: RenameLocation & Rejection | undefined; try { - const resolveLocationOperation = skeleton.resolveRenameLocation(token); + const resolveLocationOperation = skeleton.resolveRenameLocation(this._cts.token); this._progressService.showWhile(resolveLocationOperation, 250); loc = await resolveLocationOperation; } catch (e) { @@ -180,10 +164,11 @@ class RenameController extends Disposable implements IEditorContribution { return undefined; } - if (!this._activeRename || this._activeRename.id !== id) { + if (this._cts.token.isCancellationRequested) { return undefined; } + // do rename at location let selection = this.editor.getSelection(); let selectionStart = 0; let selectionEnd = loc.text.length; @@ -193,71 +178,52 @@ class RenameController extends Disposable implements IEditorContribution { selectionEnd = Math.min(loc.range.endColumn, selection.endColumn) - loc.range.startColumn; } - return this.renameInputField.getInput(loc.range, loc.text, selectionStart, selectionEnd).then(newNameOrFocusFlag => { + const newNameOrFocusFlag = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd); - if (typeof newNameOrFocusFlag === 'boolean') { - if (newNameOrFocusFlag) { - this.editor.focus(); - } - return undefined; + + if (typeof newNameOrFocusFlag === 'boolean') { + if (newNameOrFocusFlag) { + this.editor.focus(); + } + return undefined; + } + + this.editor.focus(); + + const renameOperation = raceCancellation(skeleton.provideRenameEdits(newNameOrFocusFlag, 0, [], this._cts.token), this._cts.token).then(async renameResult => { + + if (!renameResult || !this.editor.hasModel()) { + return; } - this.editor.focus(); + if (renameResult.rejectReason) { + this._notificationService.info(renameResult.rejectReason); + return; + } - const state = new EditorState(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection | CodeEditorStateFlag.Scroll); + const editResult = await this._bulkEditService.apply(renameResult, { editor: this.editor }); - const renameOperation = Promise.resolve(skeleton.provideRenameEdits(newNameOrFocusFlag, 0, [], token).then(result => { - - if (!this.editor.hasModel()) { - return undefined; - } - - if (result.rejectReason) { - if (state.validate(this.editor)) { - MessageController.get(this.editor).showMessage(result.rejectReason, this.editor.getPosition()); - } else { - this._notificationService.info(result.rejectReason); - } - return undefined; - } - - return this._bulkEditService.apply(result, { editor: this.editor }).then(result => { - // alert - if (result.ariaSummary) { - alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, result.ariaSummary)); - } - }); - - }, err => { - this._notificationService.error(nls.localize('rename.failed', "Rename failed to execute.")); - return Promise.reject(err); - })); - - this._progressService.showWhile(renameOperation, 250); - return renameOperation; + // alert + if (editResult.ariaSummary) { + alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, editResult.ariaSummary)); + } + }, err => { + this._notificationService.error(nls.localize('rename.failed', "Rename failed to execute.")); + return Promise.reject(err); }); + + this._progressService.showWhile(renameOperation, 250); + return renameOperation; + } - public acceptRenameInput(): void { - if (this._renameInputField) { - this._renameInputField.acceptInput(); - } + acceptRenameInput(): void { + this._renameInputField.getValue().acceptInput(); } - public cancelRenameInput(): void { - if (this._renameInputField) { - this._renameInputField.cancelInput(true); - } - } - - private onModelChanged(): void { - if (this._activeRename) { - this._activeRename.operation.cancel(); - this._activeRename = undefined; - - this.cancelRenameInput(); - } + cancelRenameInput(): void { + this._renameInputField.getValue().cancelInput(true); } } diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index 1113144c6c4..a87899a71a1 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -89,14 +89,14 @@ export class RenameInputField implements IContentWidget, IDisposable { const widgetShadowColor = theme.getColor(widgetShadow); const border = theme.getColor(inputBorder); - this._inputField.style.backgroundColor = background ? background.toString() : null; + this._inputField.style.backgroundColor = background ? background.toString() : ''; this._inputField.style.color = foreground ? foreground.toString() : null; this._inputField.style.borderWidth = border ? '1px' : '0px'; this._inputField.style.borderStyle = border ? 'solid' : 'none'; this._inputField.style.borderColor = border ? border.toString() : 'none'; - this._domNode!.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : null; + this._domNode!.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; } private updateFont(): void { diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index b918921b059..f0eedb8324d 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -12,33 +12,11 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; -import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelect'; import { CancellationToken } from 'vs/base/common/cancellation'; import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; - -class TestTextResourcePropertiesService implements ITextResourcePropertiesService { - - _serviceBrand: undefined; - - constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - ) { - } - - getEOL(resource: URI | undefined): string { - const filesConfiguration = this.configurationService.getValue<{ eol: string }>('files'); - if (filesConfiguration && filesConfiguration.eol) { - if (filesConfiguration.eol !== 'auto') { - return filesConfiguration.eol; - } - } - return (isLinux || isMacintosh) ? '\n' : '\r\n'; - } -} +import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/modelService.test'; class MockJSMode extends MockMode { diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index cec786f46fe..021310ed839 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -44,9 +44,9 @@ export class SnippetController2 implements IEditorContribution { return editor.getContribution('snippetController2'); } - static InSnippetMode = new RawContextKey('inSnippetMode', false); - static HasNextTabstop = new RawContextKey('hasNextTabstop', false); - static HasPrevTabstop = new RawContextKey('hasPrevTabstop', false); + static readonly InSnippetMode = new RawContextKey('inSnippetMode', false); + static readonly HasNextTabstop = new RawContextKey('hasNextTabstop', false); + static readonly HasPrevTabstop = new RawContextKey('hasPrevTabstop', false); private readonly _inSnippet: IContextKey; private readonly _hasNextTabstop: IContextKey; diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index 61e0fd2a802..7823405f14e 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -664,27 +664,23 @@ export class SnippetParser { } private _until(type: TokenType): false | string { - if (this._token.type === TokenType.EOF) { - return false; - } - let res = ''; - let pos = this._token.pos; - let prevToken = { type: TokenType.EOF, pos: 0, len: 0 }; - - while (this._token.type !== type || prevToken.type === TokenType.Backslash) { - if (this._token.type === type) { - res += this._scanner.value.substring(pos, prevToken.pos); - pos = this._token.pos; - } - prevToken = this._token; - this._token = this._scanner.next(); + const start = this._token; + while (this._token.type !== type) { if (this._token.type === TokenType.EOF) { return false; + } else if (this._token.type === TokenType.Backslash) { + const nextToken = this._scanner.next(); + if (nextToken.type !== TokenType.Dollar + && nextToken.type !== TokenType.CurlyClose + && nextToken.type !== TokenType.Backslash) { + return false; + } } + this._token = this._scanner.next(); } - res += this._scanner.value.substring(pos, this._token.pos); + const value = this._scanner.value.substring(start.pos, this._token.pos).replace(/\\(\$|}|\\)/g, '$1'); this._token = this._scanner.next(); - return res; + return value; } private _parse(marker: Marker): boolean { diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 8bc84ccfd52..91cf566e742 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -392,7 +392,7 @@ export class SnippetSession { const modelBasedVariableResolver = editor.invokeWithinContext(accessor => new ModelBasedVariableResolver(accessor.get(ILabelService, optional), model)); const clipboardService = editor.invokeWithinContext(accessor => accessor.get(IClipboardService, optional)); - clipboardText = clipboardText || clipboardService && clipboardService.readTextSync(); + const readClipboardText = () => clipboardText || clipboardService && clipboardService.readTextSync(); let delta = 0; @@ -445,7 +445,7 @@ export class SnippetSession { snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, - new ClipboardBasedVariableResolver(clipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), + new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), new SelectionBasedVariableResolver(model, selection), new CommentBasedVariableResolver(model), new TimeBasedVariableResolver, diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index 4f289c2af95..db6700822a4 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -12,8 +12,10 @@ import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snip import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad, endsWith } from 'vs/base/common/strings'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; +import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILabelService } from 'vs/platform/label/common/label'; +import { normalizeDriveLetter } from 'vs/base/common/labels'; +import { URI } from 'vs/base/common/uri'; export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({ 'CURRENT_YEAR': true, @@ -43,6 +45,7 @@ export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze( 'BLOCK_COMMENT_END': true, 'LINE_COMMENT': true, 'WORKSPACE_NAME': true, + 'WORKSPACE_FOLDER': true, }); export class CompositeSnippetVariableResolver implements VariableResolver { @@ -164,10 +167,14 @@ export class ModelBasedVariableResolver implements VariableResolver { } } +export interface IReadClipboardText { + (): string | undefined; +} + export class ClipboardBasedVariableResolver implements VariableResolver { constructor( - private readonly _clipboardText: string | undefined, + private readonly _readClipboardText: IReadClipboardText, private readonly _selectionIdx: number, private readonly _selectionCount: number, private readonly _spread: boolean @@ -180,7 +187,8 @@ export class ClipboardBasedVariableResolver implements VariableResolver { return undefined; } - if (!this._clipboardText) { + const clipboardText = this._readClipboardText(); + if (!clipboardText) { return undefined; } @@ -188,12 +196,12 @@ export class ClipboardBasedVariableResolver implements VariableResolver { // text whenever there the line count equals the cursor count // and when enabled if (this._spread) { - const lines = this._clipboardText.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s)); + const lines = clipboardText.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s)); if (lines.length === this._selectionCount) { return lines[this._selectionIdx]; } } - return this._clipboardText; + return clipboardText; } } export class CommentBasedVariableResolver implements VariableResolver { @@ -267,7 +275,7 @@ export class WorkspaceBasedVariableResolver implements VariableResolver { } resolve(variable: Variable): string | undefined { - if (variable.name !== 'WORKSPACE_NAME' || !this._workspaceService) { + if (!this._workspaceService) { return undefined; } @@ -276,6 +284,15 @@ export class WorkspaceBasedVariableResolver implements VariableResolver { return undefined; } + if (variable.name === 'WORKSPACE_NAME') { + return this._resolveWorkspaceName(workspaceIdentifier); + } else if (variable.name === 'WORKSPACE_FOLDER') { + return this._resoveWorkspacePath(workspaceIdentifier); + } + + return undefined; + } + private _resolveWorkspaceName(workspaceIdentifier: IWorkspaceIdentifier | URI): string | undefined { if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) { return path.basename(workspaceIdentifier.path); } @@ -286,4 +303,16 @@ export class WorkspaceBasedVariableResolver implements VariableResolver { } return filename; } + private _resoveWorkspacePath(workspaceIdentifier: IWorkspaceIdentifier | URI): string | undefined { + if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) { + return normalizeDriveLetter(workspaceIdentifier.fsPath); + } + + let filename = path.basename(workspaceIdentifier.configPath.path); + let folderpath = workspaceIdentifier.configPath.fsPath; + if (endsWith(folderpath, filename)) { + folderpath = folderpath.substr(0, folderpath.length - filename.length - 1); + } + return (folderpath ? normalizeDriveLetter(folderpath) : '/'); + } } diff --git a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts index f999da850bc..ba019cdc3cb 100644 --- a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts @@ -767,4 +767,17 @@ suite('SnippetParser', () => { assert.equal((variable.transform!.children[0]).ifValue, 'import { hello } from world'); assert.equal((variable.transform!.children[0]).elseValue, undefined); }); + + test('Snippet escape backslashes inside conditional insertion variable replacement #80394', function () { + + let snippet = new SnippetParser().parse('${CURRENT_YEAR/(.+)/${1:+\\\\}/}'); + let variable = snippet.children[0]; + assert.equal(snippet.children.length, 1); + assert.ok(variable instanceof Variable); + assert.ok(variable.transform); + assert.equal(variable.transform!.children.length, 1); + assert.ok(variable.transform!.children[0] instanceof FormatString); + assert.equal((variable.transform!.children[0]).ifValue, '\\'); + assert.equal((variable.transform!.children[0]).elseValue, undefined); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 0189cc0de2e..06eb5f8777c 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -236,28 +236,28 @@ suite('Snippet Variables Resolver', function () { test('Add variable to insert value from clipboard to a snippet #40153', function () { - assertVariableResolve(new ClipboardBasedVariableResolver(undefined, 1, 0, true), 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(() => undefined, 1, 0, true), 'CLIPBOARD', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver(null!, 1, 0, true), 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(() => null!, 1, 0, true), 'CLIPBOARD', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver('', 1, 0, true), 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(() => '', 1, 0, true), 'CLIPBOARD', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'CLIPBOARD', 'foo'); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'foo', 1, 0, true), 'CLIPBOARD', 'foo'); - assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'foo', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'cLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'foo', 1, 0, true), 'foo', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'foo', 1, 0, true), 'cLIPBOARD', undefined); }); test('Add variable to insert value from clipboard to a snippet #40153', function () { - assertVariableResolve(new ClipboardBasedVariableResolver('line1', 1, 2, true), 'CLIPBOARD', 'line1'); - assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2\nline3', 1, 2, true), 'CLIPBOARD', 'line1\nline2\nline3'); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1', 1, 2, true), 'CLIPBOARD', 'line1'); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2\nline3', 1, 2, true), 'CLIPBOARD', 'line1\nline2\nline3'); - assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 1, 2, true), 'CLIPBOARD', 'line2'); - resolver = new ClipboardBasedVariableResolver('line1\nline2', 0, 2, true); - assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2, true), 'CLIPBOARD', 'line1'); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2', 1, 2, true), 'CLIPBOARD', 'line2'); + resolver = new ClipboardBasedVariableResolver(() => 'line1\nline2', 0, 2, true); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2', 0, 2, true), 'CLIPBOARD', 'line1'); - assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2, false), 'CLIPBOARD', 'line1\nline2'); + assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2', 0, 2, false), 'CLIPBOARD', 'line1\nline2'); }); @@ -296,7 +296,7 @@ suite('Snippet Variables Resolver', function () { assert.equal(snippet.toString(), 'It is not line 10'); }); - test('Add workspace name variable for snippets #68261', function () { + test('Add workspace name and folder variables for snippets #68261', function () { let workspace: IWorkspace; let resolver: VariableResolver; @@ -319,14 +319,21 @@ suite('Snippet Variables Resolver', function () { // empty workspace workspace = new Workspace(''); assertVariableResolve(resolver, 'WORKSPACE_NAME', undefined); + assertVariableResolve(resolver, 'WORKSPACE_FOLDER', undefined); // single folder workspace without config workspace = new Workspace('', [toWorkspaceFolder(URI.file('/folderName'))]); assertVariableResolve(resolver, 'WORKSPACE_NAME', 'folderName'); + if (!isWindows) { + assertVariableResolve(resolver, 'WORKSPACE_FOLDER', '/folderName'); + } // workspace with config const workspaceConfigPath = URI.file('testWorkspace.code-workspace'); workspace = new Workspace('', toWorkspaceFolders([{ path: 'folderName' }], workspaceConfigPath), workspaceConfigPath); assertVariableResolve(resolver, 'WORKSPACE_NAME', 'testWorkspace'); + if (!isWindows) { + assertVariableResolve(resolver, 'WORKSPACE_FOLDER', '/'); + } }); }); diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index b5b5519fe9c..197145ad27d 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -53,7 +53,7 @@ padding-left: 20px; } -.monaco-editor .suggest-widget > .details p > code { +.monaco-editor .suggest-widget > .details p code { font-family: var(--monaco-monospace-font); } diff --git a/src/vs/editor/contrib/suggest/suggestAlternatives.ts b/src/vs/editor/contrib/suggest/suggestAlternatives.ts index e8a6ea1a338..6c9a148e618 100644 --- a/src/vs/editor/contrib/suggest/suggestAlternatives.ts +++ b/src/vs/editor/contrib/suggest/suggestAlternatives.ts @@ -11,7 +11,7 @@ import { ISelectedSuggestion } from './suggestWidget'; export class SuggestAlternatives { - static OtherSuggestions = new RawContextKey('hasOtherSuggestions', false); + static readonly OtherSuggestions = new RawContextKey('hasOtherSuggestions', false); private readonly _ckOtherSuggestions: IContextKey; diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index f29143f81dc..b8cb308422c 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -22,7 +22,7 @@ import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Context as SuggestContext, CompletionItem } from './suggest'; import { SuggestAlternatives } from './suggestAlternatives'; import { State, SuggestModel } from './suggestModel'; @@ -34,7 +34,7 @@ import { IdleValue } from 'vs/base/common/async'; import { isObject } from 'vs/base/common/types'; import { CommitCharacterController } from './suggestCommitCharacters'; import { IPosition } from 'vs/editor/common/core/position'; -import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model'; +import { TrackedRangeStickiness, ITextModel, IWordAtPosition } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import * as platform from 'vs/base/common/platform'; @@ -115,10 +115,10 @@ export class SuggestController implements IEditorContribution { const widget = this._instantiationService.createInstance(SuggestWidget, this._editor); this._toDispose.add(widget); - this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, false, true), this)); + this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, false, true, true, true), this)); // Wire up logic to accept a suggestion on certain characters - const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, false, true)); + const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, false, true, false, true)); this._toDispose.add(commitCharacterController); this._toDispose.add(this._model.onDidSuggest(e => { if (e.completionModel.items.length === 0) { @@ -223,7 +223,12 @@ export class SuggestController implements IEditorContribution { this._lineSuffix.dispose(); } - protected _insertSuggestion(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStops: boolean): void { + protected _insertSuggestion( + event: ISelectedSuggestion | undefined, + keepAlternativeSuggestions: boolean, + undoStopBefore: boolean, undoStopAfter: boolean, + keepSuffix: boolean + ): void { if (!event || !event.item) { this._alternatives.getValue().reset(); this._model.cancel(); @@ -237,12 +242,12 @@ export class SuggestController implements IEditorContribution { const model = this._editor.getModel(); const modelVersionNow = model.getAlternativeVersionId(); const { completion: suggestion, position } = event.item; - const editorColumn = this._editor.getPosition().column; - const columnDelta = editorColumn - position.column; + const editorPosition = this._editor.getPosition(); + const columnDelta = editorPosition.column - position.column; // pushing undo stops *before* additional text edits and // *after* the main edit - if (undoStops) { + if (undoStopBefore) { this._editor.pushUndoStop(); } @@ -251,16 +256,25 @@ export class SuggestController implements IEditorContribution { } // keep item in memory - this._memoryService.memorize(model, this._editor.getPosition(), event.item); + this._memoryService.memorize(model, editorPosition, event.item); let { insertText } = suggestion; if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { insertText = SnippetParser.escape(insertText); } - const overwriteBefore = position.column - suggestion.range.startColumn; - const overwriteAfter = suggestion.range.endColumn - position.column; - const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this._editor.getPosition()) : 0; + let overwriteBefore = position.column - suggestion.range.startColumn; + let overwriteAfter = suggestion.range.endColumn - position.column; + let suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(editorPosition) : 0; + let word: IWordAtPosition | null; + if (!keepSuffix) { + // don't overwrite anything right of the cursor + overwriteAfter = 0; + + } else if (overwriteAfter === 0 && (word = model.getWordAtPosition(position))) { + // compute fallback overwrite length + overwriteAfter = word.endColumn - editorPosition.column; + } SnippetController2.get(this._editor).insert(insertText, { overwriteBefore: overwriteBefore + columnDelta, @@ -270,7 +284,7 @@ export class SuggestController implements IEditorContribution { adjustWhitespace: !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace) }); - if (undoStops) { + if (undoStopAfter) { this._editor.pushUndoStop(); } @@ -300,7 +314,7 @@ export class SuggestController implements IEditorContribution { if (modelVersionNow !== model.getAlternativeVersionId()) { model.undo(); } - this._insertSuggestion(next, false, false); + this._insertSuggestion(next, false, false, false, keepSuffix); break; } }); @@ -382,7 +396,7 @@ export class SuggestController implements IEditorContribution { return; } this._editor.pushUndoStop(); - this._insertSuggestion({ index, item, model: completionModel }, true, false); + this._insertSuggestion({ index, item, model: completionModel }, true, false, false, false); }, undefined, listener); }); @@ -392,9 +406,9 @@ export class SuggestController implements IEditorContribution { this._editor.focus(); } - acceptSelectedSuggestion(keepAlternativeSuggestions?: boolean): void { + acceptSelectedSuggestion(keepAlternativeSuggestions: boolean, keepSuffix: boolean): void { const item = this._widget.getValue().getFocusedItem(); - this._insertSuggestion(item, !!keepAlternativeSuggestions, true); + this._insertSuggestion(item, keepAlternativeSuggestions, true, true, keepSuffix); } acceptNextSuggestion() { @@ -489,18 +503,37 @@ const SuggestCommand = EditorCommand.bindToContribution(Sugge registerEditorCommand(new SuggestCommand({ id: 'acceptSelectedSuggestion', precondition: SuggestContext.Visible, - handler: x => x.acceptSelectedSuggestion(true), kbOpts: { weight: weight, kbExpr: EditorContextKeys.textInputFocus, primary: KeyCode.Tab + }, + handler(x, args) { + let keepSuffix: boolean; + if (typeof args !== 'object') { + keepSuffix = true; + } else if (typeof args.keepSuffix !== 'boolean') { + keepSuffix = true; + } else { + keepSuffix = args.keepSuffix; + } + x.acceptSelectedSuggestion(true, keepSuffix); } })); +KeybindingsRegistry.registerKeybindingRule({ + id: 'acceptSelectedSuggestion', + when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus), + primary: KeyMod.Shift | KeyCode.Tab, + secondary: [KeyMod.Shift | KeyCode.Enter], + args: { keepSuffix: false }, + weight +}); + registerEditorCommand(new SuggestCommand({ id: 'acceptSelectedSuggestionOnEnter', precondition: SuggestContext.Visible, - handler: x => x.acceptSelectedSuggestion(false), + handler: x => x.acceptSelectedSuggestion(false, true), kbOpts: { weight: weight, kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit), diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index d8e629d7c03..88448ea0db7 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -676,7 +676,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { return withOracle(async (sugget, editor) => { class TestCtrl extends SuggestController { _insertSuggestion(item: ISelectedSuggestion) { - super._insertSuggestion(item, false, true); + super._insertSuggestion(item, false, true, true, false); } } const ctrl = editor.registerAndInstantiateContribution(TestCtrl); diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts index e2c2323b450..17fcaf121bf 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts @@ -23,7 +23,7 @@ export class QuickOpenEditorWidget implements IOverlayWidget { private readonly codeEditor: ICodeEditor; private readonly themeService: IThemeService; - private visible: boolean; + private visible: boolean | undefined; private quickOpenWidget: QuickOpenWidget; private domNode: HTMLElement; private styler: IDisposable; @@ -31,11 +31,8 @@ export class QuickOpenEditorWidget implements IOverlayWidget { constructor(codeEditor: ICodeEditor, onOk: () => void, onCancel: () => void, onType: (value: string) => void, configuration: IQuickOpenEditorWidgetOptions, themeService: IThemeService) { this.codeEditor = codeEditor; this.themeService = themeService; + this.visible = false; - this.create(onOk, onCancel, onType, configuration); - } - - private create(onOk: () => void, onCancel: () => void, onType: (value: string) => void, configuration: IQuickOpenEditorWidgetOptions): void { this.domNode = document.createElement('div'); this.quickOpenWidget = new QuickOpenWidget( @@ -58,29 +55,29 @@ export class QuickOpenEditorWidget implements IOverlayWidget { this.codeEditor.addOverlayWidget(this); } - public setInput(model: QuickOpenModel, focus: IAutoFocus): void { + setInput(model: QuickOpenModel, focus: IAutoFocus): void { this.quickOpenWidget.setInput(model, focus); } - public getId(): string { + getId(): string { return QuickOpenEditorWidget.ID; } - public getDomNode(): HTMLElement { + getDomNode(): HTMLElement { return this.domNode; } - public destroy(): void { + destroy(): void { this.codeEditor.removeOverlayWidget(this); this.quickOpenWidget.dispose(); this.styler.dispose(); } - public isVisible(): boolean { - return this.visible; + isVisible(): boolean { + return !!this.visible; } - public show(value: string): void { + show(value: string): void { this.visible = true; const editorLayout = this.codeEditor.getLayoutInfo(); @@ -92,13 +89,13 @@ export class QuickOpenEditorWidget implements IOverlayWidget { this.codeEditor.layoutOverlayWidget(this); } - public hide(): void { + hide(): void { this.visible = false; this.quickOpenWidget.hide(); this.codeEditor.layoutOverlayWidget(this); } - public getPosition(): IOverlayWidgetPosition | null { + getPosition(): IOverlayWidgetPosition | null { if (this.visible) { return { preference: OverlayWidgetPositionPreference.TOP_CENTER @@ -107,4 +104,4 @@ export class QuickOpenEditorWidget implements IOverlayWidget { return null; } -} \ No newline at end of file +} diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts index eeeaf6f32c8..8bd72a304a2 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -15,7 +15,7 @@ import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editor import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { DocumentSymbol, DocumentSymbolProviderRegistry, symbolKindToCssClass } from 'vs/editor/common/modes'; +import { DocumentSymbol, DocumentSymbolProviderRegistry, SymbolKinds } from 'vs/editor/common/modes'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen'; import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -198,7 +198,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { } // Add - results.push(this.symbolEntry(label, symbolKindToCssClass(element.kind), description, element.range, highlights, editor, controller)); + results.push(this.symbolEntry(label, SymbolKinds.toCssClassName(element.kind), description, element.range, highlights, editor, controller)); } } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 372ceb0deab..799e6e61e15 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -37,7 +37,7 @@ import { IKeybindingItem, KeybindingsRegistry } from 'vs/platform/keybinding/com import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label'; -import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; +import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { IProgressRunner, IEditorProgressService } from 'vs/platform/progress/common/progress'; import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -233,6 +233,8 @@ export class SimpleNotificationService implements INotificationService { public status(message: string | Error, options?: IStatusMessageOptions): IDisposable { return Disposable.None; } + + public setFilter(filter: NotificationsFilter): void { } } export class StandaloneCommandService implements ICommandService { @@ -515,12 +517,10 @@ export class SimpleResourcePropertiesService implements ITextResourcePropertiesS ) { } - getEOL(resource: URI): string { - const filesConfiguration = this.configurationService.getValue<{ eol: string }>('files'); - if (filesConfiguration && filesConfiguration.eol) { - if (filesConfiguration.eol !== 'auto') { - return filesConfiguration.eol; - } + getEOL(resource: URI, language?: string): string { + const eol = this.configurationService.getValue('files.eol', { overrideIdentifier: language, resource }); + if (eol && eol !== 'auto') { + return eol; } return (isLinux || isMacintosh) ? '\n' : '\r\n'; } @@ -551,7 +551,7 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { public _serviceBrand: undefined; - private static SCHEME = 'inmemory'; + private static readonly SCHEME = 'inmemory'; private readonly _onDidChangeWorkspaceName = new Emitter(); public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; diff --git a/src/vs/editor/standalone/common/monarch/monarchCompile.ts b/src/vs/editor/standalone/common/monarch/monarchCompile.ts index 2c98c6ba429..62f0d4313c6 100644 --- a/src/vs/editor/standalone/common/monarch/monarchCompile.ts +++ b/src/vs/editor/standalone/common/monarch/monarchCompile.ts @@ -21,18 +21,7 @@ import { IMonarchLanguage, IMonarchLanguageBracket } from 'vs/editor/standalone/ */ function isArrayOf(elemType: (x: any) => boolean, obj: any): boolean { - if (!obj) { - return false; - } - if (!(Array.isArray(obj))) { - return false; - } - for (const el of obj) { - if (!(elemType(el))) { - return false; - } - } - return true; + return Array.isArray(obj) && obj.every(elemType); } function bool(prop: any, defValue: boolean): boolean { diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 984a1e23856..0a8680d8557 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -1494,6 +1494,25 @@ suite('Editor Controller - Regression tests', () => { }); }); + test('issue #74722: Pasting whole line does not replace selection', () => { + usingCursor({ + text: [ + 'line1', + 'line sel 2', + 'line3' + ], + }, (model, cursor) => { + cursor.setSelections('test', [new Selection(2, 6, 2, 9)]); + + cursorCommand(cursor, H.Paste, { text: 'line1\n', pasteOnNewLine: true }); + + assert.equal(model.getLineContent(1), 'line1'); + assert.equal(model.getLineContent(2), 'line line1'); + assert.equal(model.getLineContent(3), ' 2'); + assert.equal(model.getLineContent(4), 'line3'); + }); + }); + test('issue #4996: Multiple cursor paste pastes contents of all cursors', () => { usingCursor({ text: [ diff --git a/src/vs/editor/test/common/core/range.test.ts b/src/vs/editor/test/common/core/range.test.ts index 1d2d627cb32..5420ae45223 100644 --- a/src/vs/editor/test/common/core/range.test.ts +++ b/src/vs/editor/test/common/core/range.test.ts @@ -87,9 +87,9 @@ suite('Editor Core - Range', () => { b = new Range(1, 1, 1, 4); assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start = b.start, a.end > b.end'); - a = new Range(1, 1, 5, 1); + a = new Range(1, 2, 5, 1); b = new Range(1, 1, 1, 4); - assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start = b.start, a.end > b.end'); + assert.ok(Range.compareRangesUsingEnds(a, b) > 0, 'a.start > b.start, a.end > b.end'); }); test('containsPosition', () => { diff --git a/src/vs/editor/test/common/core/viewLineToken.ts b/src/vs/editor/test/common/core/viewLineToken.ts index 340157110ae..d91fe77bcf0 100644 --- a/src/vs/editor/test/common/core/viewLineToken.ts +++ b/src/vs/editor/test/common/core/viewLineToken.ts @@ -5,6 +5,7 @@ import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { ColorId, TokenMetadata } from 'vs/editor/common/modes'; +import { equals } from 'vs/base/common/arrays'; /** * A token on a line. @@ -42,18 +43,8 @@ export class ViewLineToken { ); } - public static equalsArr(a: ViewLineToken[], b: ViewLineToken[]): boolean { - const aLen = a.length; - const bLen = b.length; - if (aLen !== bLen) { - return false; - } - for (let i = 0; i < aLen; i++) { - if (!this._equals(a[i], b[i])) { - return false; - } - } - return true; + public static equalsArr(a: readonly ViewLineToken[], b: readonly ViewLineToken[]): boolean { + return equals(a, b, this._equals); } } diff --git a/src/vs/editor/test/common/mocks/testConfiguration.ts b/src/vs/editor/test/common/mocks/testConfiguration.ts index 5da181c240d..b54f06ffc90 100644 --- a/src/vs/editor/test/common/mocks/testConfiguration.ts +++ b/src/vs/editor/test/common/mocks/testConfiguration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IEditorOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; @@ -33,6 +33,7 @@ export class TestConfiguration extends CommonEditorConfiguration { fontFamily: 'mockFont', fontWeight: 'normal', fontSize: 14, + fontFeatureSettings: EditorFontLigatures.OFF, lineHeight: 19, letterSpacing: 1.5, isMonospace: true, diff --git a/src/vs/editor/test/common/model/textModelSearch.test.ts b/src/vs/editor/test/common/model/textModelSearch.test.ts index aa90e1c0c3f..2a55d718b50 100644 --- a/src/vs/editor/test/common/model/textModelSearch.test.ts +++ b/src/vs/editor/test/common/model/textModelSearch.test.ts @@ -721,6 +721,20 @@ suite('TextModelSearch', () => { ); }); + test('issue #65281. \w should match line break.', () => { + assertFindMatches( + [ + 'this/is{', + 'a test', + '}', + ].join('\n'), + 'this/\\w*[^}]*', true, false, null, + [ + [1, 1, 3, 1] + ] + ); + }); + test('Simple find using unicode escape sequences', () => { assertFindMatches( regularText.join('\n'), diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts index 3f3f6880450..ed059c8a0cb 100644 --- a/src/vs/editor/test/common/modes/supports/characterPair.test.ts +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -8,6 +8,7 @@ import { StandardTokenType } from 'vs/editor/common/modes'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { find } from 'vs/base/common/arrays'; suite('CharacterPairSupport', () => { @@ -53,13 +54,8 @@ suite('CharacterPairSupport', () => { assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); }); - function findAutoClosingPair(characterPairSupport: CharacterPairSupport, character: string): StandardAutoClosingPairConditional | null { - for (const autoClosingPair of characterPairSupport.getAutoClosingPairs()) { - if (autoClosingPair.open === character) { - return autoClosingPair; - } - } - return null; + function findAutoClosingPair(characterPairSupport: CharacterPairSupport, character: string): StandardAutoClosingPairConditional | undefined { + return find(characterPairSupport.getAutoClosingPairs(), autoClosingPair => autoClosingPair.open === character); } function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, column: number): boolean { diff --git a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts index a8c5071cb9e..77f96fc20af 100644 --- a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts +++ b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts @@ -105,7 +105,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { const colorMap = [null!, '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff']; assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 0, 17, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 17, 4, true), [ '
', 'Ciao', @@ -118,7 +118,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 0, 12, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 12, 4, true), [ '
', 'Ciao', @@ -131,7 +131,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 0, 11, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 11, 4, true), [ '
', 'Ciao', @@ -143,7 +143,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 1, 11, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 1, 11, 4, true), [ '
', 'iao', @@ -155,7 +155,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 4, 11, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 4, 11, 4, true), [ '
', ' ', @@ -166,7 +166,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 5, 11, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 5, 11, 4, true), [ '
', 'hello', @@ -176,7 +176,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 5, 10, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 5, 10, 4, true), [ '
', 'hello', @@ -185,7 +185,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 6, 9, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 6, 9, 4, true), [ '
', 'ell', @@ -238,7 +238,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { const colorMap = [null!, '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff']; assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 0, 21, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 21, 4, true), [ '
', '  ', @@ -252,7 +252,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 0, 17, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 17, 4, true), [ '
', '  ', @@ -266,7 +266,7 @@ suite('Editor Modes - textToHtmlTokenizer', () => { ); assert.equal( - tokenizeLineToHTML(text, lineTokens, colorMap, 0, 3, 4), + tokenizeLineToHTML(text, lineTokens, colorMap, 0, 3, 4, true), [ '
', '  ', diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index 20d3b7d3312..0a28a285b38 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -365,7 +365,7 @@ assertComputeEdits(file1, file2); } } -class TestTextResourcePropertiesService implements ITextResourcePropertiesService { +export class TestTextResourcePropertiesService implements ITextResourcePropertiesService { _serviceBrand: undefined; @@ -375,11 +375,9 @@ class TestTextResourcePropertiesService implements ITextResourcePropertiesServic } getEOL(resource: URI, language?: string): string { - const filesConfiguration = this.configurationService.getValue<{ eol: string }>('files', { overrideIdentifier: language, resource }); - if (filesConfiguration && filesConfiguration.eol) { - if (filesConfiguration.eol !== 'auto') { - return filesConfiguration.eol; - } + const eol = this.configurationService.getValue('files.eol', { overrideIdentifier: language, resource }); + if (eol && eol !== 'auto') { + return eol; } return (platform.isLinux || platform.isMacintosh) ? '\n' : '\r\n'; } diff --git a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts index 11cc42efb8d..313c672b101 100644 --- a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { toUint32Array } from 'vs/editor/common/core/uint'; +import { toUint32Array } from 'vs/base/common/uint'; import { PrefixSumComputer, PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; suite('Editor ViewModel - PrefixSumComputer', () => { diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index d57bcaecded..a8b64c4da7a 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -9,7 +9,7 @@ import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { toUint32Array } from 'vs/editor/common/core/uint'; +import { toUint32Array } from 'vs/base/common/uint'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index cfb897053b2..70aed6e2f7d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -44,7 +44,7 @@ declare namespace monaco { constructor(parent?: CancellationToken); readonly token: CancellationToken; cancel(): void; - dispose(): void; + dispose(cancel?: boolean): void; } export interface CancellationToken { @@ -379,8 +379,8 @@ declare namespace monaco { } export interface IMarkdownString { - value: string; - isTrusted?: boolean; + readonly value: string; + readonly isTrusted?: boolean; uris?: { [href: string]: UriComponents; }; @@ -2581,7 +2581,7 @@ declare namespace monaco.editor { * Enable font ligatures. * Defaults to false. */ - fontLigatures?: boolean; + fontLigatures?: boolean | string; /** * Disable the use of `will-change` for the editor margin and lines layers. * The usage of `will-change` acts as a hint for browsers to create an extra layer. @@ -4124,6 +4124,7 @@ declare namespace monaco.editor { readonly fontFamily: string; readonly fontWeight: string; readonly fontSize: number; + readonly fontFeatureSettings: string; readonly lineHeight: number; readonly letterSpacing: number; } diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 01d88e2ece8..17dfd5efde6 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -205,19 +205,20 @@ export class MenuEntryActionViewItem extends ActionViewItem { } updateLabel(): void { - if (this.options.label) { + if (this.options.label && this.label) { this.label.textContent = this._commandAction.label; } } updateTooltip(): void { - const element = this.label; - const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id); - const keybindingLabel = keybinding && keybinding.getLabel(); + if (this.label) { + const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id); + const keybindingLabel = keybinding && keybinding.getLabel(); - element.title = keybindingLabel - ? localize('titleAndKb', "{0} ({1})", this._commandAction.label, keybindingLabel) - : this._commandAction.label; + this.label.title = keybindingLabel + ? localize('titleAndKb', "{0} ({1})", this._commandAction.label, keybindingLabel) + : this._commandAction.label; + } } updateClass(): void { @@ -249,8 +250,14 @@ export class MenuEntryActionViewItem extends ActionViewItem { MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); } - addClasses(this.label, 'icon', iconClass); - this._itemClassDispose.value = toDisposable(() => removeClasses(this.label, 'icon', iconClass)); + if (this.label) { + addClasses(this.label, 'icon', iconClass); + this._itemClassDispose.value = toDisposable(() => { + if (this.label) { + removeClasses(this.label, 'icon', iconClass); + } + }); + } } } } diff --git a/src/vs/platform/auth/common/authTokenService.ts b/src/vs/platform/auth/common/authTokenService.ts index 3f2878311af..322ecfb2b09 100644 --- a/src/vs/platform/auth/common/authTokenService.ts +++ b/src/vs/platform/auth/common/authTokenService.ts @@ -8,6 +8,7 @@ import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { Disposable } from 'vs/base/common/lifecycle'; import { IProductService } from 'vs/platform/product/common/productService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const SERVICE_NAME = 'VS Code'; const ACCOUNT = 'MyAccount'; @@ -23,9 +24,10 @@ export class AuthTokenService extends Disposable implements IAuthTokenService { constructor( @ICredentialsService private readonly credentialsService: ICredentialsService, @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, ) { super(); - if (productService.settingsSyncStoreUrl) { + if (productService.settingsSyncStoreUrl && configurationService.getValue('configurationSync.enableAuth')) { this._status = AuthTokenStatus.Inactive; this.getToken().then(token => { if (token) { diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index ab24a78884b..3d30e406440 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -28,9 +28,9 @@ export class BackupMainService implements IBackupMainService { protected backupHome: string; protected workspacesJsonPath: string; - private rootWorkspaces: IWorkspaceBackupInfo[]; - private folderWorkspaces: URI[]; - private emptyWorkspaces: IEmptyWindowBackupInfo[]; + private rootWorkspaces: IWorkspaceBackupInfo[] = []; + private folderWorkspaces: URI[] = []; + private emptyWorkspaces: IEmptyWindowBackupInfo[] = []; constructor( @IEnvironmentService environmentService: IEnvironmentService, @@ -55,8 +55,6 @@ export class BackupMainService implements IBackupMainService { } else if (Array.isArray(backups.emptyWorkspaces)) { // read legacy entries this.emptyWorkspaces = await this.validateEmptyWorkspaces(backups.emptyWorkspaces.map(backupFolder => ({ backupFolder }))); - } else { - this.emptyWorkspaces = []; } // read workspace backups @@ -70,6 +68,7 @@ export class BackupMainService implements IBackupMainService { } catch (e) { // ignore URI parsing exceptions } + this.rootWorkspaces = await this.validateWorkspaces(rootWorkspaces); // read folder backups @@ -213,14 +212,11 @@ export class BackupMainService implements IBackupMainService { } } - registerEmptyWindowBackupSync(backupFolder?: string, remoteAuthority?: string): string { + registerEmptyWindowBackupSync(backupFolderCandidate?: string, remoteAuthority?: string): string { // Generate a new folder if this is a new empty workspace - if (!backupFolder) { - backupFolder = this.getRandomEmptyWindowId(); - } - - if (!this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, backupFolder!, !platform.isLinux))) { + const backupFolder = backupFolderCandidate || this.getRandomEmptyWindowId(); + if (!this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, backupFolder, !platform.isLinux))) { this.emptyWorkspaces.push({ backupFolder, remoteAuthority }); this.saveSync(); } diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index c4749186815..452e41724c4 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -518,14 +518,14 @@ export class Configuration { return folderConsolidatedConfiguration; } - private getFolderConfigurationModelForResource(resource: URI | null | undefined, workspace: Workspace | undefined): ConfigurationModel | null { + private getFolderConfigurationModelForResource(resource: URI | null | undefined, workspace: Workspace | undefined): ConfigurationModel | undefined { if (workspace && resource) { const root = workspace.getFolder(resource); if (root) { - return types.withUndefinedAsNull(this._folderConfigurations.get(root.uri)); + return this._folderConfigurations.get(root.uri); } } - return null; + return undefined; } toData(): IConfigurationData { @@ -663,13 +663,7 @@ export class ConfigurationChangeEvent extends AbstractConfigurationChangeEvent i configurationModelsToSearch.push(...this._changedConfigurationByResource.values()); } - for (const configuration of configurationModelsToSearch) { - if (this.doesConfigurationContains(configuration, config)) { - return true; - } - } - - return false; + return configurationModelsToSearch.some(configuration => this.doesConfigurationContains(configuration, config)); } private changeWithKeys(keys: string[], resource?: URI): void { diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 8efe90bd137..41707f15c71 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -87,7 +87,7 @@ class NullContext extends Context { class ConfigAwareContextValuesContainer extends Context { - private static _keyPrefix = 'config.'; + private static readonly _keyPrefix = 'config.'; private readonly _values = new Map(); private readonly _listener: IDisposable; @@ -203,24 +203,14 @@ class SimpleContextKeyChangeEvent implements IContextKeyChangeEvent { class ArrayContextKeyChangeEvent implements IContextKeyChangeEvent { constructor(readonly keys: string[]) { } affectsSome(keys: IReadableSet): boolean { - for (const key of this.keys) { - if (keys.has(key)) { - return true; - } - } - return false; + return this.keys.some(key => keys.has(key)); } } class CompositeContextKeyChangeEvent implements IContextKeyChangeEvent { constructor(readonly events: IContextKeyChangeEvent[]) { } affectsSome(keys: IReadableSet): boolean { - for (const e of this.events) { - if (e.affectsSome(keys)) { - return true; - } - } - return false; + return this.events.some(e => e.affectsSome(keys)); } } diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 16e79df476f..ed418e1d89f 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -6,6 +6,7 @@ import { Event } from 'vs/base/common/event'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { equals } from 'vs/base/common/arrays'; export const enum ContextKeyExprType { Defined = 1, @@ -574,15 +575,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr { public equals(other: ContextKeyExpr): boolean { if (other instanceof ContextKeyAndExpr) { - if (this.expr.length !== other.expr.length) { - return false; - } - for (let i = 0, len = this.expr.length; i < len; i++) { - if (!this.expr[i].equals(other.expr[i])) { - return false; - } - } - return true; + return equals(this.expr, other.expr, (a, b) => a.equals(b)); } return false; } @@ -674,15 +667,7 @@ export class ContextKeyOrExpr implements ContextKeyExpr { public equals(other: ContextKeyExpr): boolean { if (other instanceof ContextKeyOrExpr) { - if (this.expr.length !== other.expr.length) { - return false; - } - for (let i = 0, len = this.expr.length; i < len; i++) { - if (!this.expr[i].equals(other.expr[i])) { - return false; - } - } - return true; + return equals(this.expr, other.expr, (a, b) => a.equals(b)); } return false; } diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 0a5469df8eb..461bcf07744 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -551,32 +551,32 @@ export class DiagnosticsService implements IDiagnosticsService { }); type WorkspaceStatsFileClassification = { rendererSessionId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - name: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + type: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; type WorkspaceStatsFileEvent = { rendererSessionId: string; - name: string; + type: string; count: number; }; stats.fileTypes.forEach(e => { this.telemetryService.publicLog2('workspace.stats.file', { rendererSessionId: workspace.rendererSessionId, - name: e.name, + type: e.name, count: e.count }); }); stats.launchConfigFiles.forEach(e => { this.telemetryService.publicLog2('workspace.stats.launchConfigFile', { rendererSessionId: workspace.rendererSessionId, - name: e.name, + type: e.name, count: e.count }); }); stats.configFiles.forEach(e => { this.telemetryService.publicLog2('workspace.stats.configFiles', { rendererSessionId: workspace.rendererSessionId, - name: e.name, + type: e.name, count: e.count }); }); diff --git a/src/vs/platform/dialogs/electron-main/dialogs.ts b/src/vs/platform/dialogs/electron-main/dialogs.ts index 7a38932aede..7b49ca50c2e 100644 --- a/src/vs/platform/dialogs/electron-main/dialogs.ts +++ b/src/vs/platform/dialogs/electron-main/dialogs.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, dialog, FileFilter, BrowserWindow } from 'electron'; +import { MessageBoxOptions, MessageBoxReturnValue, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, dialog, FileFilter, BrowserWindow } from 'electron'; import { Queue } from 'vs/base/common/async'; import { IStateService } from 'vs/platform/state/node/state'; import { isMacintosh } from 'vs/base/common/platform'; import { dirname } from 'vs/base/common/path'; import { normalizeNFC } from 'vs/base/common/normalization'; import { exists } from 'vs/base/node/pfs'; -import { INativeOpenDialogOptions, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue } from 'vs/platform/dialogs/node/dialogs'; +import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; import { WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces'; @@ -139,13 +139,11 @@ export class DialogMainService implements IDialogMainService { showMessageBox(options: MessageBoxOptions, window?: BrowserWindow): Promise { return this.getDialogQueue(window).queue(async () => { - return new Promise(resolve => { - if (window) { - return dialog.showMessageBox(window, options, (response, checkboxChecked) => resolve({ response, checkboxChecked })); - } + if (window) { + return dialog.showMessageBox(window, options); + } - return dialog.showMessageBox(options); - }); + return dialog.showMessageBox(options); }); } @@ -160,17 +158,16 @@ export class DialogMainService implements IDialogMainService { } return this.getDialogQueue(window).queue(async () => { - return new Promise(resolve => { - if (window) { - dialog.showSaveDialog(window, options, filePath => resolve({ filePath })); - } else { - dialog.showSaveDialog(options, filePath => resolve({ filePath })); - } - }).then(result => { - result.filePath = normalizePath(result.filePath); + let result: SaveDialogReturnValue; + if (window) { + result = await dialog.showSaveDialog(window, options); + } else { + result = await dialog.showSaveDialog(options); + } - return result; - }); + result.filePath = normalizePath(result.filePath); + + return result; }); } @@ -195,17 +192,16 @@ export class DialogMainService implements IDialogMainService { } // Show dialog - return new Promise(resolve => { - if (window) { - dialog.showOpenDialog(window, options, filePaths => resolve({ filePaths })); - } else { - dialog.showOpenDialog(options, filePaths => resolve({ filePaths })); - } - }).then(result => { - result.filePaths = normalizePaths(result.filePaths); + let result: OpenDialogReturnValue; + if (window) { + result = await dialog.showOpenDialog(window, options); + } else { + result = await dialog.showOpenDialog(options); + } - return result; - }); + result.filePaths = normalizePaths(result.filePaths); + + return result; }); } } diff --git a/src/vs/platform/dialogs/node/dialogs.ts b/src/vs/platform/dialogs/node/dialogs.ts index aa9df3c6d94..754693da712 100644 --- a/src/vs/platform/dialogs/node/dialogs.ts +++ b/src/vs/platform/dialogs/node/dialogs.ts @@ -13,16 +13,3 @@ export interface INativeOpenDialogOptions { telemetryEventName?: string; telemetryExtraData?: ITelemetryData; } - -export interface MessageBoxReturnValue { - response: number; - checkboxChecked: boolean; -} - -export interface SaveDialogReturnValue { - filePath?: string; -} - -export interface OpenDialogReturnValue { - filePaths?: string[]; -} diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index a0e9f373504..e0beffc55ed 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -19,6 +19,8 @@ import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { timeout } from 'vs/base/common/async'; import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver'; import { NativeImage } from 'electron'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { IElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; function isSilentKeyCode(keyCode: KeyCode) { return keyCode < KeyCode.KEY_0; @@ -35,7 +37,9 @@ export class Driver implements IDriver, IWindowDriverRegistry { constructor( private windowServer: IPCServer, private options: IDriverOptions, - @IWindowsMainService private readonly windowsMainService: IWindowsMainService + @IWindowsMainService private readonly windowsMainService: IWindowsMainService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, + @IElectronMainService private readonly electronMainService: IElectronMainService ) { } async registerWindowDriver(windowId: number): Promise { @@ -75,11 +79,11 @@ export class Driver implements IDriver, IWindowDriverRegistry { throw new Error('Invalid window'); } this.reloadingWindowIds.add(windowId); - this.windowsMainService.reload(window); + this.lifecycleMainService.reload(window); } async exitApplication(): Promise { - return this.windowsMainService.quit(); + return this.electronMainService.quit(undefined); } async dispatchKeybinding(windowId: number, keybinding: string): Promise { diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 71581fd6bbd..a0a3d5c4717 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -4,20 +4,28 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; -import { MessageBoxOptions, shell, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron'; +import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron'; import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { INativeOpenDialogOptions, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue } from 'vs/platform/dialogs/node/dialogs'; +import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { AddFirstParameterToFunctions } from 'vs/base/common/types'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; +import { dirExists } from 'vs/base/node/pfs'; +import { URI } from 'vs/base/common/uri'; +import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -export class ElectronMainService implements AddFirstParameterToFunctions /* only methods, not events */, number /* window ID */> { +export interface IElectronMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } + +export const IElectronMainService = createDecorator('electronMainService'); + +export class ElectronMainService implements IElectronMainService { _serviceBrand: undefined; @@ -25,7 +33,8 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + async getWindowCount(windowId: number | undefined): Promise { return this.windowsMainService.getWindowCount(); } - async getActiveWindowId(windowId: number): Promise { + async getActiveWindowId(windowId: number | undefined): Promise { const activeWindow = BrowserWindow.getFocusedWindow() || this.windowsMainService.getLastActiveWindow(); if (activeWindow) { return activeWindow.id; @@ -71,9 +80,9 @@ export class ElectronMainService implements AddFirstParameterToFunctions; - openWindow(windowId: number, toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise; - openWindow(windowId: number, arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: INativeOpenWindowOptions): Promise { + openWindow(windowId: number | undefined, options?: IOpenEmptyWindowOptions): Promise; + openWindow(windowId: number | undefined, toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise; + openWindow(windowId: number | undefined, arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: INativeOpenWindowOptions): Promise { if (Array.isArray(arg1)) { return this.doOpenWindow(windowId, arg1, arg2); } @@ -81,7 +90,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + private async doOpenWindow(windowId: number | undefined, toOpen: IWindowOpenable[], options: INativeOpenWindowOptions = Object.create(null)): Promise { if (toOpen.length > 0) { this.windowsMainService.open({ context: OpenContext.API, @@ -99,26 +108,26 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + private async doOpenEmptyWindow(windowId: number | undefined, options?: IOpenEmptyWindowOptions): Promise { this.windowsMainService.openEmptyWindow(OpenContext.API, options); } - async toggleFullScreen(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async toggleFullScreen(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { window.toggleFullScreen(); } } - async handleTitleDoubleClick(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async handleTitleDoubleClick(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { window.handleTitleDoubleClick(); } } - async isMaximized(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async isMaximized(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { return window.win.isMaximized(); } @@ -126,29 +135,29 @@ export class ElectronMainService implements AddFirstParameterToFunctions { - const window = this.windowsMainService.getWindowById(windowId); + async maximizeWindow(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { window.win.maximize(); } } - async unmaximizeWindow(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async unmaximizeWindow(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { window.win.unmaximize(); } } - async minimizeWindow(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async minimizeWindow(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { window.win.minimize(); } } - async isWindowFocused(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async isWindowFocused(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { return window.win.isFocused(); } @@ -156,12 +165,12 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + async focusWindow(windowId: number | undefined, options?: { windowId?: number; }): Promise { if (options && typeof options.windowId === 'number') { windowId = options.windowId; } - const window = this.windowsMainService.getWindowById(windowId); + const window = this.windowById(windowId); if (window) { if (isMacintosh) { window.win.show(); @@ -175,20 +184,20 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + async showMessageBox(windowId: number | undefined, options: MessageBoxOptions): Promise { return this.dialogMainService.showMessageBox(options, this.toBrowserWindow(windowId)); } - async showSaveDialog(windowId: number, options: SaveDialogOptions): Promise { + async showSaveDialog(windowId: number | undefined, options: SaveDialogOptions): Promise { return this.dialogMainService.showSaveDialog(options, this.toBrowserWindow(windowId)); } - async showOpenDialog(windowId: number, options: OpenDialogOptions): Promise { + async showOpenDialog(windowId: number | undefined, options: OpenDialogOptions): Promise { return this.dialogMainService.showOpenDialog(options, this.toBrowserWindow(windowId)); } - private toBrowserWindow(windowId: number): BrowserWindow | undefined { - const window = this.windowsMainService.getWindowById(windowId); + private toBrowserWindow(windowId: number | undefined): BrowserWindow | undefined { + const window = this.windowById(windowId); if (window) { return window.win; } @@ -196,52 +205,90 @@ export class ElectronMainService implements AddFirstParameterToFunctions { - return this.windowsMainService.pickFileFolderAndOpen(options, this.windowsMainService.getWindowById(windowId)); + async pickFileFolderAndOpen(windowId: number | undefined, options: INativeOpenDialogOptions): Promise { + const paths = await this.dialogMainService.pickFileFolder(options); + if (paths) { + this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFileFolder', options.telemetryExtraData); + this.doOpenPicked(await Promise.all(paths.map(async path => (await dirExists(path)) ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) })), options, windowId); + } } - async pickFileAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { - return this.windowsMainService.pickFileAndOpen(options, this.windowsMainService.getWindowById(windowId)); + async pickFolderAndOpen(windowId: number | undefined, options: INativeOpenDialogOptions): Promise { + const paths = await this.dialogMainService.pickFolder(options); + if (paths) { + this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFolder', options.telemetryExtraData); + this.doOpenPicked(paths.map(path => ({ folderUri: URI.file(path) })), options, windowId); + } } - async pickFolderAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { - return this.windowsMainService.pickFolderAndOpen(options, this.windowsMainService.getWindowById(windowId)); + async pickFileAndOpen(windowId: number | undefined, options: INativeOpenDialogOptions): Promise { + const paths = await this.dialogMainService.pickFile(options); + if (paths) { + this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFile', options.telemetryExtraData); + this.doOpenPicked(paths.map(path => ({ fileUri: URI.file(path) })), options, windowId); + } } - async pickWorkspaceAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { - return this.windowsMainService.pickWorkspaceAndOpen(options, this.windowsMainService.getWindowById(windowId)); + async pickWorkspaceAndOpen(windowId: number | undefined, options: INativeOpenDialogOptions): Promise { + const paths = await this.dialogMainService.pickWorkspace(options); + if (paths) { + this.sendPickerTelemetry(paths, options.telemetryEventName || 'openWorkspace', options.telemetryExtraData); + this.doOpenPicked(paths.map(path => ({ workspaceUri: URI.file(path) })), options, windowId); + } + } + + private doOpenPicked(openable: IWindowOpenable[], options: INativeOpenDialogOptions, windowId: number | undefined): void { + this.windowsMainService.open({ + context: OpenContext.DIALOG, + contextWindowId: windowId, + cli: this.environmentService.args, + urisToOpen: openable, + forceNewWindow: options.forceNewWindow + }); + } + + private sendPickerTelemetry(paths: string[], telemetryEventName: string, telemetryExtraData?: ITelemetryData) { + const numberOfPaths = paths ? paths.length : 0; + + // Telemetry + // __GDPR__TODO__ Dynamic event names and dynamic properties. Can not be registered statically. + this.telemetryService.publicLog(telemetryEventName, { + ...telemetryExtraData, + outcome: numberOfPaths ? 'success' : 'canceled', + numberOfPaths + }); } //#endregion //#region OS - async showItemInFolder(windowId: number, path: string): Promise { + async showItemInFolder(windowId: number | undefined, path: string): Promise { shell.showItemInFolder(path); } - async setRepresentedFilename(windowId: number, path: string): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async setRepresentedFilename(windowId: number | undefined, path: string): Promise { + const window = this.windowById(windowId); if (window) { window.setRepresentedFilename(path); } } - async setDocumentEdited(windowId: number, edited: boolean): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async setDocumentEdited(windowId: number | undefined, edited: boolean): Promise { + const window = this.windowById(windowId); if (window) { window.win.setDocumentEdited(edited); } } - async openExternal(windowId: number, url: string): Promise { + async openExternal(windowId: number | undefined, url: string): Promise { shell.openExternal(url); return true; } - async updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async updateTouchBar(windowId: number | undefined, items: ISerializableCommandAction[][]): Promise { + const window = this.windowById(windowId); if (window) { window.updateTouchBar(items); } @@ -279,42 +326,48 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + async relaunch(windowId: number | undefined, options?: { addArgs?: string[], removeArgs?: string[] }): Promise { return this.lifecycleMainService.relaunch(options); } - async reload(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async reload(windowId: number | undefined, options?: { disableExtensions?: boolean }): Promise { + const window = this.windowById(windowId); if (window) { - return this.windowsMainService.reload(window); + return this.lifecycleMainService.reload(window, options && options.disableExtensions ? { _: [], 'disable-extensions': true } : undefined); } } - async closeWorkspace(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); - if (window) { - return this.windowsMainService.closeWorkspace(window); - } - } - - async closeWindow(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async closeWindow(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { return window.win.close(); } } - async quit(windowId: number): Promise { - return this.windowsMainService.quit(); + async quit(windowId: number | undefined): Promise { + + // If the user selected to exit from an extension development host window, do not quit, but just + // close the window unless this is the last window that is opened. + const window = this.windowsMainService.getLastActiveWindow(); + if (window && window.isExtensionDevelopmentHost && this.windowsMainService.getWindowCount() > 1) { + window.win.close(); + } + + // Otherwise: normal quit + else { + setTimeout(() => { + this.lifecycleMainService.quit(); + }, 10 /* delay to unwind callback stack (IPC) */); + } } //#endregion //#region Connectivity - async resolveProxy(windowId: number, url: string): Promise { + async resolveProxy(windowId: number | undefined, url: string): Promise { return new Promise(resolve => { - const window = this.windowsMainService.getWindowById(windowId); + const window = this.windowById(windowId); if (window && window.win && window.win.webContents && window.win.webContents.session) { window.win.webContents.session.resolveProxy(url, proxy => resolve(proxy)); } else { @@ -327,18 +380,18 @@ export class ElectronMainService implements AddFirstParameterToFunctions { - const window = this.windowsMainService.getWindowById(windowId); + async openDevTools(windowId: number | undefined, options?: OpenDevToolsOptions): Promise { + const window = this.windowById(windowId); if (window) { window.win.webContents.openDevTools(options); } } - async toggleDevTools(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId); + async toggleDevTools(windowId: number | undefined): Promise { + const window = this.windowById(windowId); if (window) { const contents = window.win.webContents; - if (isMacintosh && window.hasHiddenTitleBarStyle() && !window.isFullScreen() && !contents.isDevToolsOpened()) { + if (isMacintosh && window.hasHiddenTitleBarStyle && !window.isFullScreen && !contents.isDevToolsOpened()) { contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 } else { contents.toggleDevTools(); @@ -346,7 +399,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + async startCrashReporter(windowId: number | undefined, options: CrashReporterStartOptions): Promise { crashReporter.start(options); } @@ -356,7 +409,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + async openExtensionDevelopmentHostWindow(windowId: number | undefined, args: ParsedArgs, env: IProcessEnvironment): Promise { const extDevPaths = args.extensionDevelopmentPath; if (extDevPaths) { this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, { @@ -368,4 +421,12 @@ export class ElectronMainService implements AddFirstParameterToFunctions; - reload(): Promise; - closeWorkspace(): Promise; + reload(options?: { disableExtensions?: boolean }): Promise; closeWindow(): Promise; quit(): Promise; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 789bb4c805f..99a9753a4df 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -71,7 +71,6 @@ export interface ParsedArgs { 'driver-verbose'?: boolean; remote?: string; 'disable-user-env-probe'?: boolean; - 'disable-inspect'?: boolean; 'force'?: boolean; 'force-user-env'?: boolean; @@ -116,7 +115,7 @@ export interface IEnvironmentService { settingsResource: URI; keybindingsResource: URI; keyboardLayoutResource: URI; - localeResource: URI; + argvResource: URI; // sync resources userDataSyncLogResource: URI; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index e61113171d0..5b49fd0dd46 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -109,7 +109,6 @@ export const OPTIONS: OptionDescriptions> = { 'trace': { type: 'boolean' }, 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, - 'disable-inspect': { type: 'boolean' }, 'force-user-env': { type: 'boolean' }, 'js-flags': { type: 'string' }, // chrome js flags @@ -304,7 +303,6 @@ export function buildVersionMessage(version: string | undefined, commit: string return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`; } - export function addArg(argv: string[], ...args: string[]): string[] { const endOfArgsMarkerIndex = argv.indexOf('--'); if (endOfArgsMarkerIndex === -1) { diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index f7d207009de..5c2b8296750 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -95,7 +95,6 @@ export class EnvironmentService implements IEnvironmentService { @memoize get userDataPath(): string { const vscodePortable = process.env['VSCODE_PORTABLE']; - if (vscodePortable) { return path.join(vscodePortable, 'user-data'); } @@ -141,7 +140,14 @@ export class EnvironmentService implements IEnvironmentService { get keyboardLayoutResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } @memoize - get localeResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'locale.json'); } + get argvResource(): URI { + const vscodePortable = process.env['VSCODE_PORTABLE']; + if (vscodePortable) { + return URI.file(path.join(vscodePortable, 'argv.json')); + } + + return URI.file(path.join(this.userHome, product.dataFolderName, 'argv.json')); + } @memoize get isExtensionDevelopment(): boolean { return !!this._args.extensionDevelopmentPath; } @@ -182,7 +188,6 @@ export class EnvironmentService implements IEnvironmentService { } const vscodePortable = process.env['VSCODE_PORTABLE']; - if (vscodePortable) { return path.join(vscodePortable, 'extensions'); } diff --git a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts index f12dfef68bc..3fff232b834 100644 --- a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts @@ -17,6 +17,7 @@ import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher'; import { IWatcherRequest, IWatcherService, IWatcherOptions } from 'vs/platform/files/node/watcher/unix/watcher'; import { Emitter, Event } from 'vs/base/common/event'; +import { equals } from 'vs/base/common/arrays'; interface IWatcher { requests: ExtendedWatcherRequest[]; @@ -32,8 +33,8 @@ export class ChokidarWatcherService implements IWatcherService { private static readonly FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms) private static readonly EVENT_SPAM_WARNING_THRESHOLD = 60 * 1000; // warn after certain time span of event spam - private _watchers: { [watchPath: string]: IWatcher }; - private _watcherCount: number; + private _watchers: { [watchPath: string]: IWatcher } = Object.create(null); + private _watcherCount = 0; private _pollingInterval?: number; private _usePolling?: boolean; @@ -351,26 +352,10 @@ export function normalizeRoots(requests: IWatcherRequest[]): { [basePath: string return result; } -function isEqualRequests(r1: IWatcherRequest[], r2: IWatcherRequest[]) { - if (r1.length !== r2.length) { - return false; - } - for (let k = 0; k < r1.length; k++) { - if (r1[k].path !== r2[k].path || !isEqualIgnore(r1[k].excludes, r2[k].excludes)) { - return false; - } - } - return true; +function isEqualRequests(r1: readonly IWatcherRequest[], r2: readonly IWatcherRequest[]) { + return equals(r1, r2, (a, b) => a.path === b.path && isEqualIgnore(a.excludes, b.excludes)); } -function isEqualIgnore(i1: string[], i2: string[]) { - if (i1.length !== i2.length) { - return false; - } - for (let k = 0; k < i1.length; k++) { - if (i1[k] !== i2[k]) { - return false; - } - } - return true; +function isEqualIgnore(i1: readonly string[], i2: readonly string[]) { + return equals(i1, i2); } diff --git a/src/vs/platform/files/test/node/diskFileService.test.ts b/src/vs/platform/files/test/node/diskFileService.test.ts index 3f5c57ec1a8..5c69ce8fdcb 100644 --- a/src/vs/platform/files/test/node/diskFileService.test.ts +++ b/src/vs/platform/files/test/node/diskFileService.test.ts @@ -21,19 +21,13 @@ import { isLinux, isWindows } from 'vs/base/common/platform'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; import { VSBuffer, VSBufferReadable, toVSBufferReadableStream, VSBufferReadableStream, bufferToReadable, bufferToStream } from 'vs/base/common/buffer'; +import { find } from 'vs/base/common/arrays'; -function getByName(root: IFileStat, name: string): IFileStat | null { +function getByName(root: IFileStat, name: string): IFileStat | undefined { if (root.children === undefined) { - return null; + return undefined; } - - for (const child of root.children) { - if (child.name === name) { - return child; - } - } - - return null; + return find(root.children, child => child.name === name); } function toLineByLineReadable(content: string): VSBufferReadable { diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 5345229bdc1..44be0d24797 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -219,13 +219,23 @@ export class InstantiationService implements IInstantiationService { // Return a proxy object that's backed by an idle value. That // strategy is to instantiate services in our idle time or when actually // needed but not when injected into a consumer - const idle = new IdleValue(() => this._createInstance(ctor, args, _trace)); + const idle = new IdleValue(() => this._createInstance(ctor, args, _trace)); return new Proxy(Object.create(null), { - get(_target: T, prop: PropertyKey): any { - return (idle.getValue() as any)[prop]; + get(target: any, key: PropertyKey): any { + if (key in target) { + return target[key]; + } + let obj = idle.getValue(); + let prop = obj[key]; + if (typeof prop !== 'function') { + return prop; + } + prop = prop.bind(obj); + target[key] = prop; + return prop; }, set(_target: T, p: PropertyKey, value: any): boolean { - (idle.getValue() as any)[p] = value; + idle.getValue()[p] = value; return true; } }); @@ -241,7 +251,7 @@ const enum TraceType { class Trace { - private static _None = new class extends Trace { + private static readonly _None = new class extends Trace { constructor() { super(-1, null); } stop() { } branch() { return this; } diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index e41d966ea84..975cb234e2e 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import * as objects from 'vs/base/common/objects'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue'; -import { BrowserWindow, ipcMain, screen, Event as IpcMainEvent, Display, shell } from 'electron'; +import { BrowserWindow, ipcMain, screen, IpcMainEvent, Display, shell } from 'electron'; import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; import { PerformanceInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 3c30daf3ab4..5c3de02794b 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -38,6 +38,7 @@ export interface IKeybindings { export interface IKeybindingRule extends IKeybindings { id: string; weight: number; + args?: any; when: ContextKeyExpr | null | undefined; } @@ -132,7 +133,7 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { if (actualKb && actualKb.primary) { const kk = createKeybinding(actualKb.primary, OS); if (kk) { - this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, 0, rule.when); + this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, 0, rule.when); } } @@ -141,7 +142,7 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { const k = actualKb.secondary[i]; const kk = createKeybinding(k, OS); if (kk) { - this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, -i - 1, rule.when); + this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, -i - 1, rule.when); } } } diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index 84a31d13438..73f80cb4f68 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -159,7 +159,8 @@ suite('AbstractKeybindingService', () => { statusMessageCallsDisposed!.push(message); } }; - } + }, + setFilter() { } }; let resolver = new KeybindingResolver(items, []); diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 9c94d83e152..8fb2fa26bc1 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -188,7 +188,7 @@ export class LaunchMainService implements ILaunchMainService { // In addition, we poll for the wait marker file to be deleted to return. if (waitMarkerFileURI && usedWindows.length === 1 && usedWindows[0]) { return Promise.race([ - this.windowsMainService.waitForWindowCloseOrLoad(usedWindows[0].id), + usedWindows[0].whenClosedOrLoaded, whenDeleted(waitMarkerFileURI.fsPath) ]).then(() => undefined, () => undefined); } @@ -219,7 +219,7 @@ export class LaunchMainService implements ILaunchMainService { mainPID: process.pid, mainArguments: process.argv.slice(1), windows, - screenReader: !!app.isAccessibilitySupportEnabled(), + screenReader: !!app.accessibilitySupportEnabled, gpuFeatureStatus: app.getGPUFeatureStatus() }); } @@ -234,7 +234,8 @@ export class LaunchMainService implements ILaunchMainService { const windows = this.windowsMainService.getWindows(); const promises: Promise[] = windows.map(window => { return new Promise((resolve, reject) => { - if (window.remoteAuthority) { + const remoteAuthority = window.remoteAuthority; + if (remoteAuthority) { const replyChannel = `vscode:getDiagnosticInfoResponse${window.id}`; const args: IDiagnosticInfoOptions = { includeProcesses: options.includeProcesses, @@ -246,14 +247,14 @@ export class LaunchMainService implements ILaunchMainService { ipcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => { // No data is returned if getting the connection fails. if (!data) { - resolve({ hostName: window.remoteAuthority!, errorMessage: `Unable to resolve connection to '${window.remoteAuthority}'.` }); + resolve({ hostName: remoteAuthority, errorMessage: `Unable to resolve connection to '${remoteAuthority}'.` }); } resolve(data); }); setTimeout(() => { - resolve({ hostName: window.remoteAuthority!, errorMessage: `Fetching remote diagnostics for '${window.remoteAuthority}' timed out.` }); + resolve({ hostName: remoteAuthority, errorMessage: `Fetching remote diagnostics for '${remoteAuthority}' timed out.` }); }, 5000); } else { resolve(); diff --git a/src/vs/platform/lifecycle/common/lifecycleService.ts b/src/vs/platform/lifecycle/common/lifecycleService.ts index ea8b52b2492..1b2cb90c8af 100644 --- a/src/vs/platform/lifecycle/common/lifecycleService.ts +++ b/src/vs/platform/lifecycle/common/lifecycleService.ts @@ -23,7 +23,7 @@ export abstract class AbstractLifecycleService extends Disposable implements ILi protected readonly _onShutdown = this._register(new Emitter()); readonly onShutdown: Event = this._onShutdown.event; - protected _startupKind: StartupKind; + protected _startupKind: StartupKind = StartupKind.NewWindow; get startupKind(): StartupKind { return this._startupKind; } private _phase: LifecyclePhase = LifecyclePhase.Starting; diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 17253677571..d907bf1e03f 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -13,6 +13,7 @@ import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { Barrier } from 'vs/base/common/async'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; export const ILifecycleMainService = createDecorator('lifecycleMainService'); @@ -82,6 +83,11 @@ export interface ILifecycleMainService { */ readonly onBeforeWindowUnload: Event; + /** + * Reload a window. All lifecycle event handlers are triggered. + */ + reload(window: ICodeWindow, cli?: ParsedArgs): Promise; + /** * Unload a window for the provided reason. All lifecycle event handlers are triggered. */ @@ -360,6 +366,15 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe }); } + async reload(window: ICodeWindow, cli?: ParsedArgs): Promise { + + // Only reload when the window has not vetoed this + const veto = await this.unload(window, UnloadReason.RELOAD); + if (!veto) { + window.reload(undefined, cli); + } + } + async unload(window: ICodeWindow, reason: UnloadReason): Promise { // Always allow to unload a window that is not yet ready diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index e3c42e0b054..c0a6c5b2c0d 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -430,7 +430,7 @@ export class WorkbenchTree extends Tree { constructor( container: HTMLElement, configuration: ITreeConfiguration, - options: ITreeOptions, + options: ITreeOptions | undefined, @IContextKeyService contextKeyService: IContextKeyService, @IListService listService: IListService, @IThemeService themeService: IThemeService, @@ -881,9 +881,9 @@ export class WorkbenchAsyncDataTree extends Async ) { const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService); super(user, container, delegate, renderers, dataSource, treeOptions); - this.disposables.push(disposable); + this.disposables.add(disposable); this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService); - this.disposables.push(this.internals); + this.disposables.add(this.internals); } } @@ -910,9 +910,9 @@ export class WorkbenchCompressibleAsyncDataTree e ) { const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService); super(user, container, virtualDelegate, compressionDelegate, renderers, dataSource, treeOptions); - this.disposables.push(disposable); + this.disposables.add(disposable); this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService); - this.disposables.push(this.internals); + this.disposables.add(this.internals); } } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 0f00ac45280..53d102a2eca 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { isMacintosh, language } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, Event as KeyboardEvent } from 'electron'; +import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, KeyboardEvent } from 'electron'; import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle, IRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/windows/common/windows'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -22,6 +22,7 @@ import { URI } from 'vs/base/common/uri'; import { IStateService } from 'vs/platform/state/node/state'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; +import { IElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; const telemetryFrom = 'menu'; @@ -43,8 +44,8 @@ export class Menubar { private static readonly lastKnownMenubarStorageKey = 'lastKnownMenubarData'; - private willShutdown: boolean; - private appMenuInstalled: boolean; + private willShutdown: boolean | undefined; + private appMenuInstalled: boolean | undefined; private closedLastWindow: boolean; private menuUpdater: RunOnceScheduler; @@ -69,7 +70,8 @@ export class Menubar { @IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService, @IStateService private readonly stateService: IStateService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IElectronMainService private readonly electronMainService: IElectronMainService ) { this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0); @@ -111,8 +113,8 @@ export class Menubar { // File Menu Items this.fallbackMenuHandlers['workbench.action.files.newUntitledFile'] = () => this.windowsMainService.openEmptyWindow(OpenContext.MENU); this.fallbackMenuHandlers['workbench.action.newWindow'] = () => this.windowsMainService.openEmptyWindow(OpenContext.MENU); - this.fallbackMenuHandlers['workbench.action.files.openFileFolder'] = (menuItem, win, event) => this.windowsMainService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); - this.fallbackMenuHandlers['workbench.action.openWorkspace'] = (menuItem, win, event) => this.windowsMainService.pickWorkspaceAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); + this.fallbackMenuHandlers['workbench.action.files.openFileFolder'] = (menuItem, win, event) => this.electronMainService.pickFileFolderAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); + this.fallbackMenuHandlers['workbench.action.openWorkspace'] = (menuItem, win, event) => this.electronMainService.pickWorkspaceAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); // Recent Menu Items this.fallbackMenuHandlers['workbench.action.clearRecentFiles'] = () => this.workspacesHistoryMainService.clearRecentlyOpened(); @@ -359,16 +361,17 @@ export class Menubar { const servicesMenu = new Menu(); const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu }); const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' }); - const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' }); + const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideOthers', accelerator: 'Command+Alt+H' }); const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' }); const quit = new MenuItem(this.likeAction('workbench.action.quit', { label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => { + const lastActiveWindow = this.windowsMainService.getLastActiveWindow(); if ( - this.windowsMainService.getWindowCount() === 0 || // allow to quit when no more windows are open - !!BrowserWindow.getFocusedWindow() || // allow to quit when window has focus (fix for https://github.com/Microsoft/vscode/issues/39191) - this.windowsMainService.getLastActiveWindow()!.isMinimized() // allow to quit when window has no focus but is minimized (https://github.com/Microsoft/vscode/issues/63000) + this.windowsMainService.getWindowCount() === 0 || // allow to quit when no more windows are open + !!BrowserWindow.getFocusedWindow() || // allow to quit when window has focus (fix for https://github.com/Microsoft/vscode/issues/39191) + (lastActiveWindow && lastActiveWindow.isMinimized()) // allow to quit when window has no focus but is minimized (https://github.com/Microsoft/vscode/issues/63000) ) { - this.windowsMainService.quit(); + this.electronMainService.quit(undefined); } } })); diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index b16fe281725..663ba2070bf 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -227,6 +227,20 @@ export interface IStatusMessageOptions { hideAfter?: number; } +export enum NotificationsFilter { + + /** + * No filter is enabled. + */ + OFF, + + /** + * All notifications are configured as silent. See + * `INotificationProperties.silent` for more info. + */ + SILENT +} + /** * A service to bring up notifications and non-modal prompts. * @@ -286,6 +300,13 @@ export interface INotificationService { * @returns a disposable to hide the status message */ status(message: NotificationMessage, options?: IStatusMessageOptions): IDisposable; + + /** + * Allows to configure a filter for notifications. + * + * @param filter the filter to use + */ + setFilter(filter: NotificationsFilter): void; } export class NoOpNotification implements INotificationHandle { diff --git a/src/vs/platform/notification/test/common/testNotificationService.ts b/src/vs/platform/notification/test/common/testNotificationService.ts index b4c4d4000fd..671339e55e9 100644 --- a/src/vs/platform/notification/test/common/testNotificationService.ts +++ b/src/vs/platform/notification/test/common/testNotificationService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotificationService, INotificationHandle, NoOpNotification, Severity, INotification, IPromptChoice, IPromptOptions, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotificationHandle, NoOpNotification, Severity, INotification, IPromptChoice, IPromptOptions, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; export class TestNotificationService implements INotificationService { @@ -35,4 +35,6 @@ export class TestNotificationService implements INotificationService { status(message: string | Error, options?: IStatusMessageOptions): IDisposable { return Disposable.None; } -} \ No newline at end of file + + setFilter(filter: NotificationsFilter): void { } +} diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index f44dcee0455..5aa5c32d7ea 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -56,8 +56,6 @@ export interface IProductConfiguration { readonly productName: string; }; - readonly welcomePage?: string; - readonly enableTelemetry?: boolean; readonly aiConfig?: { readonly asimovKey: string; diff --git a/src/vs/platform/severityIcon/common/severityIcon.ts b/src/vs/platform/severityIcon/common/severityIcon.ts index 52eae1a08cc..946313a389e 100644 --- a/src/vs/platform/severityIcon/common/severityIcon.ts +++ b/src/vs/platform/severityIcon/common/severityIcon.ts @@ -4,70 +4,57 @@ *--------------------------------------------------------------------------------------------*/ import Severity from 'vs/base/common/severity'; -import { registerThemingParticipant, ITheme, LIGHT } from 'vs/platform/theme/common/themeService'; -import { Color } from 'vs/base/common/color'; - -const errorStart = encodeURIComponent(``); -const errorDarkStart = encodeURIComponent(``); - -const warningStart = encodeURIComponent(``); -const warningDarkStart = encodeURIComponent(``); - -const infoStart = encodeURIComponent(``); -const infoDarkStart = encodeURIComponent(``); +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { problemsErrorIconForeground, problemsInfoIconForeground, problemsWarningIconForeground } from 'vs/platform/theme/common/colorRegistry'; export namespace SeverityIcon { - export function getSVGData(severity: Severity, theme: ITheme): string { - switch (severity) { - case Severity.Ignore: - const ignoreColor = theme.type === LIGHT ? Color.fromHex('#75BEFF') : Color.fromHex('#007ACC'); - return theme.type === LIGHT ? infoStart + encodeURIComponent(ignoreColor.toString()) + infoEnd - : infoDarkStart + encodeURIComponent(ignoreColor.toString()) + infoDarkEnd; - case Severity.Info: - const infoColor = theme.type === LIGHT ? Color.fromHex('#007ACC') : Color.fromHex('#75BEFF'); - return theme.type === LIGHT ? infoStart + encodeURIComponent(infoColor.toString()) + infoEnd - : infoDarkStart + encodeURIComponent(infoColor.toString()) + infoDarkEnd; - case Severity.Warning: - const warningColor = theme.type === LIGHT ? Color.fromHex('#DDB100') : Color.fromHex('#fc0'); - return theme.type === LIGHT ? warningStart + encodeURIComponent(warningColor.toString()) + warningEnd - : warningDarkStart + encodeURIComponent(warningColor.toString()) + warningDarkEnd; - case Severity.Error: - const errorColor = theme.type === LIGHT ? Color.fromHex('#A1260D') : Color.fromHex('#F48771'); - return theme.type === LIGHT ? errorStart + encodeURIComponent(errorColor.toString()) + errorEnd - : errorDarkStart + encodeURIComponent(errorColor.toString()) + errorDarkEnd; - } - return ''; - } - export function className(severity: Severity): string { switch (severity) { case Severity.Ignore: - return 'severity-icon severity-ignore'; + return 'severity-ignore codicon-info'; case Severity.Info: - return 'severity-icon severity-info'; + return 'codicon-info'; case Severity.Warning: - return 'severity-icon severity-warning'; + return 'codicon-warning'; case Severity.Error: - return 'severity-icon severity-error'; + return 'codicon-error'; } return ''; } } -function getCSSRule(severity: Severity, theme: ITheme): string { - return `.${SeverityIcon.className(severity).split(' ').join('.')} { background: url("data:image/svg+xml,${SeverityIcon.getSVGData(severity, theme)}") center center no-repeat; height: 16px; width: 16px; }`; -} - registerThemingParticipant((theme, collector) => { - collector.addRule(getCSSRule(Severity.Error, theme)); - collector.addRule(getCSSRule(Severity.Warning, theme)); - collector.addRule(getCSSRule(Severity.Info, theme)); - collector.addRule(getCSSRule(Severity.Ignore, theme)); -}); \ No newline at end of file + + const errorIconForeground = theme.getColor(problemsErrorIconForeground); + if (errorIconForeground) { + collector.addRule(` + .monaco-workbench .zone-widget .codicon-error, + .monaco-workbench .markers-panel .marker-icon.codicon-error, + .monaco-workbench .extensions-viewlet > .extensions .codicon-error { + color: ${errorIconForeground}; + } + `); + } + + const warningIconForeground = theme.getColor(problemsWarningIconForeground); + if (errorIconForeground) { + collector.addRule(` + .monaco-workbench .zone-widget .codicon-warning, + .monaco-workbench .markers-panel .marker-icon.codicon-warning, + .monaco-workbench .extensions-viewlet > .extensions .codicon-warning { + color: ${warningIconForeground}; + } + `); + } + + const infoIconForeground = theme.getColor(problemsInfoIconForeground); + if (errorIconForeground) { + collector.addRule(` + .monaco-workbench .zone-widget .codicon-info, + .monaco-workbench .markers-panel .marker-icon.codicon-info { + color: ${infoIconForeground}; + } + `); + } +}); diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index 5f0a6d0cdc3..d5146ec25ac 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -127,7 +127,7 @@ export class StateService implements IStateService { _serviceBrand: undefined; - private static STATE_FILE = 'storage.json'; + private static readonly STATE_FILE = 'storage.json'; private fileStorage: FileStorage; diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 67f3e6ac6d0..e1da1af90f8 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -15,6 +15,7 @@ import { joinPath } from 'vs/base/common/resources'; import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async'; import { serializableToMap, mapToSerializable } from 'vs/base/common/map'; import { VSBuffer } from 'vs/base/common/buffer'; +import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; export class BrowserStorageService extends Disposable implements IStorageService { @@ -26,20 +27,20 @@ export class BrowserStorageService extends Disposable implements IStorageService private readonly _onWillSaveState: Emitter = this._register(new Emitter()); readonly onWillSaveState: Event = this._onWillSaveState.event; - private globalStorage: IStorage; - private workspaceStorage: IStorage; + private globalStorage: IStorage | undefined; + private workspaceStorage: IStorage | undefined; - private globalStorageDatabase: FileStorageDatabase; - private workspaceStorageDatabase: FileStorageDatabase; + private globalStorageDatabase: FileStorageDatabase | undefined; + private workspaceStorageDatabase: FileStorageDatabase | undefined; - private globalStorageFile: URI; - private workspaceStorageFile: URI; + private globalStorageFile: URI | undefined; + private workspaceStorageFile: URI | undefined; - private initializePromise: Promise; + private initializePromise: Promise | undefined; private periodicSaveScheduler = this._register(new RunOnceScheduler(() => this.collectState(), 5000)); get hasPendingUpdate(): boolean { - return this.globalStorageDatabase.hasPendingUpdate || this.workspaceStorageDatabase.hasPendingUpdate; + return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate); } constructor( @@ -137,16 +138,18 @@ export class BrowserStorageService extends Disposable implements IStorageService } private getStorage(scope: StorageScope): IStorage { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); } async logStorage(): Promise { + const [globalStorage, workspaceStorage, globalStorageFile, workspaceStorageFile] = assertAllDefined(this.globalStorage, this.workspaceStorage, this.globalStorageFile, this.workspaceStorageFile); + const result = await Promise.all([ - this.globalStorage.items, - this.workspaceStorage.items + globalStorage.items, + workspaceStorage.items ]); - return logStorage(result[0], result[1], this.globalStorageFile.toString(), this.workspaceStorageFile.toString()); + return logStorage(result[0], result[1], globalStorageFile.toString(), workspaceStorageFile.toString()); } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index f74005b5ed4..b0a2b3752a5 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -29,7 +29,7 @@ interface ISerializableItemsChangeEvent { export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel { - private static STORAGE_CHANGE_DEBOUNCE_TIME = 100; + private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; private readonly _onDidChangeItems: Emitter = this._register(new Emitter()); readonly onDidChangeItems: Event = this._onDidChangeItems.event; diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index 70cdeef730d..81c9511a239 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -87,7 +87,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic _serviceBrand: undefined; - private static STORAGE_NAME = 'state.vscdb'; + private static readonly STORAGE_NAME = 'state.vscdb'; private readonly _onDidChangeStorage: Emitter = this._register(new Emitter()); readonly onDidChangeStorage: Event = this._onDidChangeStorage.event; diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index c0c777db385..bdd7b454af1 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -15,13 +15,14 @@ import { copy, exists, mkdirp, writeFile } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; export class NativeStorageService extends Disposable implements IStorageService { _serviceBrand: undefined; - private static WORKSPACE_STORAGE_NAME = 'state.vscdb'; - private static WORKSPACE_META_NAME = 'workspace.json'; + private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; + private static readonly WORKSPACE_META_NAME = 'workspace.json'; private readonly _onDidChangeStorage: Emitter = this._register(new Emitter()); readonly onDidChangeStorage: Event = this._onDidChangeStorage.event; @@ -31,11 +32,11 @@ export class NativeStorageService extends Disposable implements IStorageService private globalStorage: IStorage; - private workspaceStoragePath: string; - private workspaceStorage: IStorage; - private workspaceStorageListener: IDisposable; + private workspaceStoragePath: string | undefined; + private workspaceStorage: IStorage | undefined; + private workspaceStorageListener: IDisposable | undefined; - private initializePromise: Promise; + private initializePromise: Promise | undefined; constructor( globalStorageDatabase: IStorageDatabase, @@ -191,22 +192,24 @@ export class NativeStorageService extends Disposable implements IStorageService // Do it await Promise.all([ - this.globalStorage.close(), - this.workspaceStorage.close() + this.getStorage(StorageScope.GLOBAL).close(), + this.getStorage(StorageScope.WORKSPACE).close() ]); } private getStorage(scope: StorageScope): IStorage { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); } async logStorage(): Promise { + const [workspaceStorage, workspaceStoragePath] = assertAllDefined(this.workspaceStorage, this.workspaceStoragePath); + const result = await Promise.all([ this.globalStorage.items, - this.workspaceStorage.items + workspaceStorage.items ]); - logStorage(result[0], result[1], this.environmentService.globalStorageHome, this.workspaceStoragePath); + logStorage(result[0], result[1], this.environmentService.globalStorageHome, workspaceStoragePath); } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { @@ -215,7 +218,7 @@ export class NativeStorageService extends Disposable implements IStorageService } // Close workspace DB to be able to copy - await this.workspaceStorage.close(); + await this.getStorage(StorageScope.WORKSPACE).close(); // Prepare new workspace storage folder const result = await this.prepareWorkspaceStorageFolder(toWorkspace); @@ -223,7 +226,7 @@ export class NativeStorageService extends Disposable implements IStorageService const newWorkspaceStoragePath = join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME); // Copy current storage over to new workspace storage - await copy(this.workspaceStoragePath, newWorkspaceStoragePath); + await copy(assertIsDefined(this.workspaceStoragePath), newWorkspaceStoragePath); // Recreate and init workspace storage return this.createWorkspaceStorage(newWorkspaceStoragePath).init(); diff --git a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts index ac0da4361f7..d70af9beefb 100644 --- a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts @@ -13,8 +13,16 @@ export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; import * as Platform from 'vs/base/common/platform'; import * as uuid from 'vs/base/common/uuid'; import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils'; +import { mixin } from 'vs/base/common/objects'; -export async function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string | undefined, version: string | undefined, machineId: string, remoteAuthority?: string): Promise<{ [name: string]: string | undefined }> { +export async function resolveWorkbenchCommonProperties( + storageService: IStorageService, + commit: string | undefined, + version: string | undefined, + machineId: string, + remoteAuthority?: string, + resolveAdditionalProperties?: () => { [key: string]: any } +): Promise<{ [name: string]: string | undefined }> { const result: { [name: string]: string | undefined; } = Object.create(null); const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; @@ -68,6 +76,10 @@ export async function resolveWorkbenchCommonProperties(storageService: IStorageS } }); + if (resolveAdditionalProperties) { + mixin(result, resolveAdditionalProperties()); + } + return result; } diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 1e468a2018d..8e64e8e045a 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -24,8 +24,8 @@ export interface ITelemetryServiceConfig { export class TelemetryService implements ITelemetryService { - static IDLE_START_EVENT_NAME = 'UserIdleStart'; - static IDLE_STOP_EVENT_NAME = 'UserIdleStop'; + static readonly IDLE_START_EVENT_NAME = 'UserIdleStart'; + static readonly IDLE_STOP_EVENT_NAME = 'UserIdleStop'; _serviceBrand: undefined; diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index a8399a43946..520c211b51b 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -20,7 +20,7 @@ export const NullTelemetryService = new class implements ITelemetryService { return this.publicLog(eventName, data as ITelemetryData); } setEnabled() { } - isOptedIn: true; + isOptedIn = true; getTelemetryInfo(): Promise { return Promise.resolve({ instanceId: 'someValue.instanceId', diff --git a/src/vs/platform/telemetry/node/commonProperties.ts b/src/vs/platform/telemetry/node/commonProperties.ts index 4d5c2cfd1b7..d681c0c7733 100644 --- a/src/vs/platform/telemetry/node/commonProperties.ts +++ b/src/vs/platform/telemetry/node/commonProperties.ts @@ -7,7 +7,6 @@ import * as Platform from 'vs/base/common/platform'; import * as os from 'os'; import * as uuid from 'vs/base/common/uuid'; import { readFile } from 'vs/base/node/pfs'; -import { mixin } from 'vs/base/common/objects'; export async function resolveCommonProperties( commit: string | undefined, @@ -15,8 +14,7 @@ export async function resolveCommonProperties( machineId: string | undefined, msftInternalDomains: string[] | undefined, installSourcePath: string, - product?: string, - resolveAdditionalProperties?: () => { [key: string]: any } + product?: string ): Promise<{ [name: string]: string | boolean | undefined; }> { const result: { [name: string]: string | boolean | undefined; } = Object.create(null); @@ -41,7 +39,7 @@ export async function resolveCommonProperties( const msftInternal = verifyMicrosoftInternalDomain(msftInternalDomains || []); if (msftInternal) { - // __GDPR__COMMON__ "common.msftInternal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + // __GDPR__COMMON__ "common.msftInternal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } result['common.msftInternal'] = msftInternal; } @@ -80,24 +78,14 @@ export async function resolveCommonProperties( // ignore error } - if (resolveAdditionalProperties) { - mixin(result, resolveAdditionalProperties()); - } - return result; } -function verifyMicrosoftInternalDomain(domainList: string[]): boolean { +function verifyMicrosoftInternalDomain(domainList: readonly string[]): boolean { if (!process || !process.env || !process.env['USERDNSDOMAIN']) { return false; } const domain = process.env['USERDNSDOMAIN']!.toLowerCase(); - for (let msftDomain of domainList) { - if (domain === msftDomain) { - return true; - } - } - - return false; + return domainList.some(msftDomain => domain === msftDomain); } diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index 637090a6896..41b8fffcfc4 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -15,10 +15,9 @@ export async function resolveWorkbenchCommonProperties( machineId: string, msftInternalDomains: string[] | undefined, installSourcePath: string, - remoteAuthority?: string, - resolveAdditionalProperties?: () => { [key: string]: any } + remoteAuthority?: string ): Promise<{ [name: string]: string | boolean | undefined }> { - const result = await resolveCommonProperties(commit, version, machineId, msftInternalDomains, installSourcePath, undefined, resolveAdditionalProperties); + const result = await resolveCommonProperties(commit, version, machineId, msftInternalDomains, installSourcePath, undefined); const instanceId = storageService.get(instanceStorageKey, StorageScope.GLOBAL)!; const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; diff --git a/src/vs/platform/telemetry/test/browser/commonProperties.test.ts b/src/vs/platform/telemetry/test/browser/commonProperties.test.ts new file mode 100644 index 00000000000..6355efaaac4 --- /dev/null +++ b/src/vs/platform/telemetry/test/browser/commonProperties.test.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as assert from 'assert'; +import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/browser/workbenchCommonProperties'; +import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; + +suite('Browser Telemetry - common properties', function () { + + const commit: string = (undefined)!; + const version: string = (undefined)!; + let testStorageService: IStorageService; + + setup(() => { + testStorageService = new InMemoryStorageService(); + }); + + test('mixes in additional properties', async function () { + const resolveCommonTelemetryProperties = () => { + return { + 'userId': '1' + }; + }; + + const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, resolveCommonTelemetryProperties); + + assert.ok('commitHash' in props); + assert.ok('sessionID' in props); + assert.ok('timestamp' in props); + assert.ok('common.platform' in props); + assert.ok('common.timesincesessionstart' in props); + assert.ok('common.sequence' in props); + assert.ok('version' in props); + assert.ok('common.firstSessionDate' in props, 'firstSessionDate'); + assert.ok('common.lastSessionDate' in props, 'lastSessionDate'); + assert.ok('common.isNewSession' in props, 'isNewSession'); + assert.ok('common.machineId' in props, 'machineId'); + + assert.equal(props['userId'], '1'); + }); + + test('mixes in additional dyanmic properties', async function () { + let i = 1; + const resolveCommonTelemetryProperties = () => { + return Object.defineProperties({}, { + 'userId': { + get: () => { + return i++; + }, + enumerable: true + } + }); + }; + + const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, resolveCommonTelemetryProperties); + assert.equal(props['userId'], '1'); + + const props2 = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, resolveCommonTelemetryProperties); + assert.equal(props2['userId'], '2'); + }); +}); diff --git a/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts b/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts index 138cd35a1fa..ee49c04ba3f 100644 --- a/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts @@ -81,52 +81,4 @@ suite('Telemetry - common properties', function () { value2 = props['common.timesincesessionstart']; assert.ok(value1 !== value2, 'timesincesessionstart'); }); - - test('mixes in additional properties', async function () { - const resolveCommonTelemetryProperties = () => { - return { - 'userId': '1' - }; - }; - - const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, installSource, undefined, resolveCommonTelemetryProperties); - - assert.ok('commitHash' in props); - assert.ok('sessionID' in props); - assert.ok('timestamp' in props); - assert.ok('common.platform' in props); - assert.ok('common.nodePlatform' in props); - assert.ok('common.nodeArch' in props); - assert.ok('common.timesincesessionstart' in props); - assert.ok('common.sequence' in props); - assert.ok('common.platformVersion' in props, 'platformVersion'); - assert.ok('version' in props); - assert.ok('common.firstSessionDate' in props, 'firstSessionDate'); - assert.ok('common.lastSessionDate' in props, 'lastSessionDate'); - assert.ok('common.isNewSession' in props, 'isNewSession'); - assert.ok('common.instanceId' in props, 'instanceId'); - assert.ok('common.machineId' in props, 'machineId'); - - assert.equal(props['userId'], '1'); - }); - - test('mixes in additional dyanmic properties', async function () { - let i = 1; - const resolveCommonTelemetryProperties = () => { - return Object.defineProperties({}, { - 'userId': { - get: () => { - return i++; - }, - enumerable: true - } - }); - }; - - const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, installSource, undefined, resolveCommonTelemetryProperties); - assert.equal(props['userId'], '1'); - - const props2 = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, installSource, undefined, resolveCommonTelemetryProperties); - assert.equal(props2['userId'], '2'); - }); }); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index e73b91f4277..6d8af22abb9 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -404,8 +404,11 @@ export const overviewRulerFindMatchForeground = registerColor('editorOverviewRul export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerSelectionHighlightForeground', 'Overview ruler marker color for selection highlights. The color must not be opaque so as not to hide underlying decorations.'), true); export const minimapFindMatch = registerColor('minimap.findMatchHighlight', { light: '#d18616', dark: '#d18616', hc: '#AB5A00' }, nls.localize('minimapFindMatchHighlight', 'Minimap marker color for find matches.'), true); -export const minimapSelection = registerColor('minimap.selectionHighlight', { light: '#ADD6FF', dark: '#264F78', hc: '#f3f518' }, nls.localize('minimapSelectionHighlight', 'Minimap marker color for the current editor selection.'), true); +export const minimapSelection = registerColor('minimap.selectionHighlight', { light: '#ADD6FF', dark: '#264F78', hc: '#ffffff' }, nls.localize('minimapSelectionHighlight', 'Minimap marker color for the editor selection.'), true); +export const problemsErrorIconForeground = registerColor('problemsErrorIcon.foreground', { dark: editorErrorForeground, light: editorErrorForeground, hc: editorErrorForeground }, nls.localize('problemsErrorIconForeground', "The color used for the problems error icon.")); +export const problemsWarningIconForeground = registerColor('problemsWarningIcon.foreground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground }, nls.localize('problemsWarningIconForeground', "The color used for the problems warning icon.")); +export const problemsInfoIconForeground = registerColor('problemsInfoIcon.foreground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoForeground }, nls.localize('problemsInfoIconForeground', "The color used for the problems info icon.")); // ----- color functions @@ -473,7 +476,7 @@ function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, /** * @param colorValue Resolve a color value in the context of a theme */ -function resolveColorValue(colorValue: ColorValue | null, theme: ITheme): Color | undefined { +export function resolveColorValue(colorValue: ColorValue | null, theme: ITheme): Color | undefined { if (colorValue === null) { return undefined; } else if (typeof colorValue === 'string') { diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 8ce034bd94c..56f0deef229 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -20,7 +20,7 @@ export interface IThemable { } export interface IColorMapping { - [optionsKey: string]: ColorIdentifier | ColorFunction | undefined; + [optionsKey: string]: ColorValue | undefined; } export interface IComputedStyles { @@ -30,11 +30,9 @@ export interface IComputedStyles { export function computeStyles(theme: ITheme, styleMap: IColorMapping): IComputedStyles { const styles = Object.create(null) as IComputedStyles; for (let key in styleMap) { - const value = styleMap[key as string]; - if (typeof value === 'string') { - styles[key] = theme.getColor(value); - } else if (typeof value === 'function') { - styles[key] = value(theme); + const value = styleMap[key]; + if (value) { + styles[key] = resolveColorValue(value, theme); } } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index cd73a5cd95a..d25c577b66e 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as electron from 'electron'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -20,7 +20,7 @@ export class DarwinUpdateService extends AbstractUpdateService { _serviceBrand: undefined; - private disposables: IDisposable[] = []; + private readonly disposables = new DisposableStore(); @memoize private get onRawError(): Event { return Event.fromNodeEventEmitter(electron.autoUpdater, 'error', (_, message) => message); } @memoize private get onRawUpdateNotAvailable(): Event { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-not-available'); } @@ -104,6 +104,6 @@ export class DarwinUpdateService extends AbstractUpdateService { } dispose(): void { - this.disposables = dispose(this.disposables); + this.disposables.dispose(); } } diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index 479bc0bca73..097a2398e9b 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -9,8 +9,19 @@ import { IDisposable } from 'vs/base/common/lifecycle'; export const IURLService = createDecorator('urlService'); +export interface IOpenURLOptions { + + /** + * If not provided or `false`, signals that the + * URL to open did not originate from the product + * but outside. As such, a confirmation dialog + * might be shown to the user. + */ + trusted?: boolean; +} + export interface IURLHandler { - handleURL(uri: URI): Promise; + handleURL(uri: URI, options?: IOpenURLOptions): Promise; } export interface IURLService { @@ -24,7 +35,7 @@ export interface IURLService { */ create(options?: Partial): URI; - open(url: URI): Promise; + open(url: URI, options?: IOpenURLOptions): Promise; registerHandler(handler: IURLHandler): IDisposable; } diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/common/urlIpc.ts index 84396c2400e..88a3d20e1d0 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/common/urlIpc.ts @@ -6,7 +6,7 @@ import { IChannel, IServerChannel, IClientRouter, IConnectionHub, Client } from 'vs/base/parts/ipc/common/ipc'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { IURLHandler } from 'vs/platform/url/common/url'; +import { IURLHandler, IOpenURLOptions } from 'vs/platform/url/common/url'; import { CancellationToken } from 'vs/base/common/cancellation'; import { first } from 'vs/base/common/arrays'; @@ -31,7 +31,7 @@ export class URLHandlerChannelClient implements IURLHandler { constructor(private channel: IChannel) { } - handleURL(uri: URI): Promise { + handleURL(uri: URI, options?: IOpenURLOptions): Promise { return this.channel.call('handleURL', uri.toJSON()); } } diff --git a/src/vs/platform/url/common/urlService.ts b/src/vs/platform/url/common/urlService.ts index 322929c723d..030d52b587e 100644 --- a/src/vs/platform/url/common/urlService.ts +++ b/src/vs/platform/url/common/urlService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; +import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/common/url'; import { URI, UriComponents } from 'vs/base/common/uri'; import { values } from 'vs/base/common/map'; import { first } from 'vs/base/common/async'; @@ -17,9 +17,9 @@ export abstract class AbstractURLService extends Disposable implements IURLServi abstract create(options?: Partial): URI; - open(uri: URI): Promise { + open(uri: URI, options?: IOpenURLOptions): Promise { const handlers = values(this.handlers); - return first(handlers.map(h => () => h.handleURL(uri)), undefined, false).then(val => val || false); + return first(handlers.map(h => () => h.handleURL(uri, options)), undefined, false).then(val => val || false); } registerHandler(handler: IURLHandler): IDisposable { diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index fbb4c595415..54fcb05a56c 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -70,16 +70,16 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser async sync(): Promise { if (!this.configurationService.getValue('configurationSync.enableExtensions')) { - this.logService.trace('Extensions: Skipping synchronising extensions as it is disabled.'); + this.logService.trace('Extensions: Skipping synchronizing extensions as it is disabled.'); return false; } if (this.status !== SyncStatus.Idle) { - this.logService.trace('Extensions: Skipping synchronising extensions as it is running already.'); + this.logService.trace('Extensions: Skipping synchronizing extensions as it is running already.'); return false; } - this.logService.trace('Extensions: Started synchronising extensions...'); + this.logService.trace('Extensions: Started synchronizing extensions...'); this.setStatus(SyncStatus.Syncing); try { @@ -88,13 +88,13 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser this.setStatus(SyncStatus.Idle); if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Rejected) { // Rejected as there is a new remote version. Syncing again, - this.logService.info('Extensions: Failed to synchronise extensions as there is a new remote version available. Synchronising again...'); + this.logService.info('Extensions: Failed to synchronise extensions as there is a new remote version available. Synchronizing again...'); return this.sync(); } throw e; } - this.logService.trace('Extensions: Finised synchronising extensions.'); + this.logService.trace('Extensions: Finised synchronizing extensions.'); this.setStatus(SyncStatus.Idle); return true; } @@ -129,7 +129,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser const { added, removed, updated, remote } = this.merge(localExtensions, remoteExtensions, lastSyncExtensions); if (!added.length && !removed.length && !updated.length && !remote) { - this.logService.trace('Extensions: No changes found during synchronising extensions.'); + this.logService.trace('Extensions: No changes found during synchronizing extensions.'); } if (added.length || removed.length || updated.length) { @@ -162,7 +162,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser const ignoredExtensions = this.configurationService.getValue('configurationSync.extensionsToIgnore') || []; // First time sync if (!remoteExtensions) { - this.logService.info('Extensions: Remote extensions does not exist. Synchronising extensions for the first time.'); + this.logService.info('Extensions: Remote extensions does not exist. Synchronizing extensions for the first time.'); return { added: [], removed: [], updated: [], remote: localExtensions.filter(({ identifier }) => ignoredExtensions.some(id => id.toLowerCase() === identifier.id.toLowerCase())) }; } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 69a7fb4f650..49f4176c018 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -81,27 +81,27 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { async sync(_continue?: boolean): Promise { if (!this.configurationService.getValue('configurationSync.enableSettings')) { - this.logService.trace('Settings: Skipping synchronising settings as it is disabled.'); + this.logService.trace('Settings: Skipping synchronizing settings as it is disabled.'); return false; } if (_continue) { - this.logService.info('Settings: Resumed synchronising settings'); + this.logService.info('Settings: Resumed synchronizing settings'); return this.continueSync(); } if (this.status !== SyncStatus.Idle) { - this.logService.trace('Settings: Skipping synchronising settings as it is running already.'); + this.logService.trace('Settings: Skipping synchronizing settings as it is running already.'); return false; } - this.logService.trace('Settings: Started synchronising settings...'); + this.logService.trace('Settings: Started synchronizing settings...'); this.setStatus(SyncStatus.Syncing); try { const result = await this.getPreview(); if (result.hasConflicts) { - this.logService.info('Settings: Detected conflicts while synchronising settings.'); + this.logService.info('Settings: Detected conflicts while synchronizing settings.'); this.setStatus(SyncStatus.HasConflicts); return false; } @@ -112,12 +112,12 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { this.setStatus(SyncStatus.Idle); if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Rejected) { // Rejected as there is a new remote version. Syncing again, - this.logService.info('Settings: Failed to synchronise settings as there is a new remote version available. Synchronising again...'); + this.logService.info('Settings: Failed to synchronise settings as there is a new remote version available. Synchronizing again...'); return this.sync(); } if (e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) { // Rejected as there is a new local version. Syncing again. - this.logService.info('Settings: Failed to synchronise settings as there is a new local version available. Synchronising again...'); + this.logService.info('Settings: Failed to synchronise settings as there is a new local version available. Synchronizing again...'); return this.sync(); } throw e; @@ -128,7 +128,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { if (this.syncPreviewResultPromise) { this.syncPreviewResultPromise.cancel(); this.syncPreviewResultPromise = null; - this.logService.info('Settings: Stopped synchronising settings.'); + this.logService.info('Settings: Stopped synchronizing settings.'); } this.fileService.del(this.environmentService.settingsSyncPreviewResource); this.setStatus(SyncStatus.Idle); @@ -158,7 +158,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise; if (!hasLocalChanged && !hasRemoteChanged) { - this.logService.trace('Settings: No changes found during synchronising settings.'); + this.logService.trace('Settings: No changes found during synchronizing settings.'); } if (hasLocalChanged) { this.logService.info('Settings: Updating local settings'); @@ -178,10 +178,10 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { // Delete the preview await this.fileService.del(this.environmentService.settingsSyncPreviewResource); } else { - this.logService.trace('Settings: No changes found during synchronising settings.'); + this.logService.trace('Settings: No changes found during synchronizing settings.'); } - this.logService.trace('Settings: Finised synchronising settings.'); + this.logService.trace('Settings: Finised synchronizing settings.'); this.syncPreviewResultPromise = null; this.setStatus(SyncStatus.Idle); } @@ -235,7 +235,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { // First time syncing to remote else if (fileContent) { - this.logService.info('Settings: Remote settings does not exist. Synchronising settings for the first time.'); + this.logService.info('Settings: Remote settings does not exist. Synchronizing settings for the first time.'); hasRemoteChanged = true; previewContent = fileContent.value.toString(); } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 942a4a3b318..0ac8e4c8373 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -38,13 +38,13 @@ export function registerConfiguration(): IDisposable { }, 'configurationSync.enableSettings': { type: 'boolean', - description: localize('configurationSync.enableSettings', "When enabled settings are synchronised while synchronising configuration."), + description: localize('configurationSync.enableSettings', "When enabled settings are synchronised while synchronizing configuration."), default: true, scope: ConfigurationScope.APPLICATION, }, 'configurationSync.enableExtensions': { type: 'boolean', - description: localize('configurationSync.enableExtensions', "When enabled extensions are synchronised while synchronising configuration."), + description: localize('configurationSync.enableExtensions', "When enabled extensions are synchronised while synchronizing configuration."), default: true, scope: ConfigurationScope.APPLICATION, }, @@ -63,6 +63,12 @@ export function registerConfiguration(): IDisposable { $ref: ignoredSettingsSchemaId, additionalProperties: true, uniqueItems: true + }, + 'configurationSync.enableAuth': { + 'type': 'boolean', + description: localize('configurationSync.enableAuth', "Enables authentication and requires VS Code restart when changed"), + 'default': false, + 'scope': ConfigurationScope.APPLICATION } } }); @@ -85,6 +91,7 @@ export interface IUserData { } export enum UserDataSyncStoreErrorCode { + Unauthroized = 'Unauthroized', Rejected = 'Rejected', Unknown = 'Unknown' } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 8c7d6684359..69823ebce3c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, IUserDataSyncLogService, UserDataSyncStoreError, UserDataSyncStoreErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; @@ -127,7 +127,7 @@ export class UserDataAutoSync extends Disposable { @IConfigurationService private readonly configurationService: IConfigurationService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, - @IUserDataSyncLogService private readonly userDataSyncLogService: IUserDataSyncLogService, + @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IAuthTokenService private readonly authTokenService: IAuthTokenService, ) { super(); @@ -147,13 +147,13 @@ export class UserDataAutoSync extends Disposable { this.enabled = enabled; if (this.enabled) { - this.userDataSyncLogService.info('Syncing configuration started'); + this.logService.info('Syncing configuration started'); this.sync(true); return; } else { if (stopIfDisabled) { this.userDataSyncService.stop(); - this.userDataSyncLogService.info('Syncing configuration stopped.'); + this.logService.info('Syncing configuration stopped.'); } } @@ -164,7 +164,14 @@ export class UserDataAutoSync extends Disposable { try { await this.userDataSyncService.sync(); } catch (e) { - this.userDataSyncLogService.error(e); + if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Unauthroized) { + if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Unauthroized && this.authTokenService.status === AuthTokenStatus.Disabled) { + this.logService.error('Sync failed because the server requires authorization. Please enable authorization.'); + } else { + this.logService.error(e); + } + } + this.logService.error(e); } if (loop) { await timeout(1000 * 5); // Loop sync for every 5s. diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 4c49f62988e..bfcc8fab3cb 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, } from 'vs/base/common/lifecycle'; -import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService, asText, isSuccess } from 'vs/platform/request/common/request'; import { URI } from 'vs/base/common/uri'; @@ -22,7 +22,6 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn constructor( @IProductService private readonly productService: IProductService, @IRequestService private readonly requestService: IRequestService, - @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IAuthTokenService private readonly authTokenService: IAuthTokenService, ) { super(); @@ -100,10 +99,11 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn const context = await this.requestService.request(options, token); if (context.res.statusCode === 401) { - // Not Authorized - this.logService.info('Authroization Failed.'); - this.authTokenService.refreshToken(); - Promise.reject('Authroization Failed.'); + if (this.authTokenService.status !== AuthTokenStatus.Disabled) { + this.authTokenService.refreshToken(); + } + // Throw Unauthorized Error + throw new UserDataSyncStoreError('Unauthorized', UserDataSyncStoreErrorCode.Unauthroized); } return context; diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 5dd270d8c25..9d519473683 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { OpenContext, IWindowConfiguration, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -13,6 +12,7 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; import { Rectangle, BrowserWindow } from 'electron'; +import { IDisposable } from 'vs/base/common/lifecycle'; export interface IWindowState { width?: number; @@ -30,10 +30,16 @@ export const enum WindowMode { Fullscreen } -export interface ICodeWindow { +export interface ICodeWindow extends IDisposable { + + readonly onClose: Event; + readonly onDestroy: Event; + + readonly whenClosedOrLoaded: Promise; + readonly id: number; readonly win: BrowserWindow; - readonly config: IWindowConfiguration; + readonly config: IWindowConfiguration | undefined; readonly openedFolderUri?: URI; readonly openedWorkspace?: IWorkspaceIdentifier; @@ -48,6 +54,9 @@ export interface ICodeWindow { readonly isReady: boolean; ready(): Promise; + setReady(): void; + + readonly hasHiddenTitleBarStyle: boolean; addTabbedWindow(window: ICodeWindow): void; @@ -62,20 +71,19 @@ export interface ICodeWindow { send(channel: string, ...args: any[]): void; sendWhenReady(channel: string, ...args: any[]): void; + readonly isFullScreen: boolean; toggleFullScreen(): void; - isFullScreen(): boolean; + isMinimized(): boolean; - hasHiddenTitleBarStyle(): boolean; + setRepresentedFilename(name: string): void; - getRepresentedFilename(): string; + getRepresentedFilename(): string | undefined; + handleTitleDoubleClick(): void; updateTouchBar(items: ISerializableCommandAction[][]): void; - setReady(): void; serializeWindowState(): IWindowState; - - dispose(): void; } export const IWindowsMainService = createDecorator('windowsMainService'); @@ -91,17 +99,11 @@ export interface IWindowsMainService { readonly onWindowReady: Event; readonly onWindowsCountChanged: Event; - readonly onWindowClose: Event; open(openConfig: IOpenConfiguration): ICodeWindow[]; openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[]; openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[]; - pickFileFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; - pickFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; - pickFileAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; - pickWorkspaceAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; - sendToFocused(channel: string, ...args: any[]): void; sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void; @@ -110,11 +112,6 @@ export interface IWindowsMainService { getWindowById(windowId: number): ICodeWindow | undefined; getWindows(): ICodeWindow[]; getWindowCount(): number; - - waitForWindowCloseOrLoad(windowId: number): Promise; - reload(win: ICodeWindow, cli?: ParsedArgs): void; - closeWorkspace(win: ICodeWindow): void; - quit(): void; } export interface IOpenConfiguration { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts similarity index 84% rename from src/vs/code/electron-main/windows.ts rename to src/vs/platform/windows/electron-main/windowsMainService.ts index 61638921926..173db4ee066 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -19,23 +19,19 @@ import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMai import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, IPathsToWaitFor, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/platform/windows/node/window'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import product from 'vs/platform/product/common/product'; -import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; import { IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { dirExists } from 'vs/base/node/pfs'; import { getComparisonKey, isEqual, normalizePath, originalFSPath, hasTrailingPathSeparator, removeTrailingPathSeparator } from 'vs/base/common/resources'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; -import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/code/electron-main/windowsStateStorage'; +import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/platform/windows/electron-main/windowsStateStorage'; import { getWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -44,11 +40,6 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { isWindowsDriveLetter, toSlashes } from 'vs/base/common/extpath'; import { CharCode } from 'vs/base/common/charCode'; -const enum WindowError { - UNRESPONSIVE = 1, - CRASHED = 2 -} - export interface IWindowState { workspace?: IWorkspaceIdentifier; folderUri?: URI; @@ -158,7 +149,7 @@ interface IWorkspacePathToOpen { label?: string; } -export class WindowsManager extends Disposable implements IWindowsMainService { +export class WindowsMainService extends Disposable implements IWindowsMainService { _serviceBrand: undefined; @@ -175,9 +166,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService { private readonly _onWindowClose = this._register(new Emitter()); readonly onWindowClose: CommonEvent = this._onWindowClose.event; - private readonly _onWindowLoad = this._register(new Emitter()); - readonly onWindowLoad: CommonEvent = this._onWindowLoad.event; - private readonly _onWindowsCountChanged = this._register(new Emitter()); readonly onWindowsCountChanged: CommonEvent = this._onWindowsCountChanged.event; @@ -189,7 +177,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService { @IEnvironmentService private readonly environmentService: IEnvironmentService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, - @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService, @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, @@ -198,7 +185,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { ) { super(); - this.windowsState = restoreWindowsState(this.stateService.getItem(WindowsManager.windowsStateStorageKey)); + this.windowsState = restoreWindowsState(this.stateService.getItem(WindowsMainService.windowsStateStorageKey)); if (!Array.isArray(this.windowsState.openedWindows)) { this.windowsState.openedWindows = []; } @@ -312,7 +299,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { if (!currentWindowsState.lastActiveWindow) { let activeWindow = this.getLastActiveWindow(); if (!activeWindow || activeWindow.isExtensionDevelopmentHost) { - activeWindow = WindowsManager.WINDOWS.filter(window => !window.isExtensionDevelopmentHost)[0]; + activeWindow = WindowsMainService.WINDOWS.filter(window => !window.isExtensionDevelopmentHost)[0]; } if (activeWindow) { @@ -321,7 +308,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } // 2.) Find extension host window - const extensionHostWindow = WindowsManager.WINDOWS.filter(window => window.isExtensionDevelopmentHost && !window.isExtensionTestHost)[0]; + const extensionHostWindow = WindowsMainService.WINDOWS.filter(window => window.isExtensionDevelopmentHost && !window.isExtensionTestHost)[0]; if (extensionHostWindow) { currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow); } @@ -332,11 +319,11 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // so if we ever want to persist the UI state of the last closed window (window count === 1), it has // to come from the stored lastClosedWindowState on Win/Linux at least if (this.getWindowCount() > 1) { - currentWindowsState.openedWindows = WindowsManager.WINDOWS.filter(window => !window.isExtensionDevelopmentHost).map(window => this.toWindowState(window)); + currentWindowsState.openedWindows = WindowsMainService.WINDOWS.filter(window => !window.isExtensionDevelopmentHost).map(window => this.toWindowState(window)); } // Persist - this.stateService.setItem(WindowsManager.windowsStateStorageKey, getWindowsStateStoreData(currentWindowsState)); + this.stateService.setItem(WindowsMainService.windowsStateStorageKey, getWindowsStateStoreData(currentWindowsState)); } // See note on #onBeforeShutdown() for details how these events are flowing @@ -382,6 +369,19 @@ export class WindowsManager extends Disposable implements IWindowsMainService { }; } + openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[] { + let cli = this.environmentService.args; + const remote = options && options.remoteAuthority; + if (cli && (cli.remote !== remote)) { + cli = { ...cli, remote }; + } + + const forceReuseWindow = options && options.forceReuseWindow; + const forceNewWindow = !forceReuseWindow; + + return this.open({ context, cli, forceEmpty: true, forceNewWindow, forceReuseWindow }); + } + open(openConfig: IOpenConfiguration): ICodeWindow[] { this.logService.trace('windowsManager#open'); openConfig = this.validateOpenConfig(openConfig); @@ -459,7 +459,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // 1.) focus last active window if we are not instructed to open any paths if (focusLastActive) { - const lastActiveWindow = usedWindows.filter(window => window.backupPath === this.windowsState.lastActiveWindow!.backupPath); + const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath); if (lastActiveWindow.length) { lastActiveWindow[0].focus(); focusLastOpened = false; @@ -472,9 +472,9 @@ export class WindowsManager extends Disposable implements IWindowsMainService { for (let i = usedWindows.length - 1; i >= 0; i--) { const usedWindow = usedWindows[i]; if ( - (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.workspace.id === usedWindow.openedWorkspace!.id)) || // skip over restored workspace - (usedWindow.openedFolderUri && foldersToRestore.some(uri => isEqual(uri, usedWindow.openedFolderUri))) || // skip over restored folder - (usedWindow.backupPath && emptyToRestore.some(empty => empty.backupFolder === basename(usedWindow.backupPath!))) // skip over restored empty window + (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => usedWindow.openedWorkspace && workspace.workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace + (usedWindow.openedFolderUri && foldersToRestore.some(uri => isEqual(uri, usedWindow.openedFolderUri))) || // skip over restored folder + (usedWindow.backupPath && emptyToRestore.some(empty => usedWindow.backupPath && empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window ) { continue; } @@ -512,7 +512,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // process can continue. We do this by deleting the waitMarkerFilePath. const waitMarkerFileURI = openConfig.waitMarkerFileURI; if (openConfig.context === OpenContext.CLI && waitMarkerFileURI && usedWindows.length === 1 && usedWindows[0]) { - this.waitForWindowCloseOrLoad(usedWindows[0].id).then(() => fs.unlink(waitMarkerFileURI.fsPath, _error => undefined)); + usedWindows[0].whenClosedOrLoaded.then(() => fs.unlink(waitMarkerFileURI.fsPath, _error => undefined)); } return usedWindows; @@ -559,7 +559,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0]; // only look at the windows with correct authority - const windows = WindowsManager.WINDOWS.filter(window => window.remoteAuthority === fileInputs!.remoteAuthority); + const windows = WindowsMainService.WINDOWS.filter(window => fileInputs && window.remoteAuthority === fileInputs.remoteAuthority); const bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, @@ -615,7 +615,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { if (allWorkspacesToOpen.length > 0) { // Check for existing instances - const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, workspaceToOpen.workspace))); + const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace))); if (windowsOnWorkspace.length > 0) { const windowOnWorkspace = windowsOnWorkspace[0]; const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : undefined; @@ -633,7 +633,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // Open remaining ones allWorkspacesToOpen.forEach(workspaceToOpen => { - if (windowsOnWorkspace.some(win => win.openedWorkspace!.id === workspaceToOpen.workspace.id)) { + if (windowsOnWorkspace.some(win => win.openedWorkspace && win.openedWorkspace.id === workspaceToOpen.workspace.id)) { return; // ignore folders that are already open } @@ -657,7 +657,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { if (allFoldersToOpen.length > 0) { // Check for existing instances - const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, folderToOpen.folderUri))); + const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri))); if (windowsOnFolderPath.length > 0) { const windowOnFolderPath = windowsOnFolderPath[0]; const fileInputsForWindow = fileInputs && fileInputs.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : undefined; @@ -1229,9 +1229,9 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // Reload an existing extension development host window on the same path // We currently do not allow more than one extension development window // on the same extension path. - const existingWindow = findWindowOnExtensionDevelopmentPath(WindowsManager.WINDOWS, extensionDevelopmentPath); + const existingWindow = findWindowOnExtensionDevelopmentPath(WindowsMainService.WINDOWS, extensionDevelopmentPath); if (existingWindow) { - this.reload(existingWindow, openConfig.cli); + this.lifecycleMainService.reload(existingWindow, openConfig.cli); existingWindow.focus(); // make sure it gets focus and is restored return [existingWindow]; @@ -1283,7 +1283,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { cliArgs = cliArgs.filter(path => { const uri = URI.file(path); - if (!!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, uri)) { + if (!!findWindowOnWorkspaceOrFolderUri(WindowsMainService.WINDOWS, uri)) { return false; } return uri.authority === authority; @@ -1291,7 +1291,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { folderUris = folderUris.filter(uri => { const u = this.argToUri(uri); - if (!!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, u)) { + if (!!findWindowOnWorkspaceOrFolderUri(WindowsMainService.WINDOWS, u)) { return false; } return u ? u.authority === authority : false; @@ -1299,7 +1299,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { fileUris = fileUris.filter(uri => { const u = this.argToUri(uri); - if (!!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, u)) { + if (!!findWindowOnWorkspaceOrFolderUri(WindowsMainService.WINDOWS, u)) { return false; } return u ? u.authority === authority : false; @@ -1388,7 +1388,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } // Create the window - window = this.instantiationService.createInstance(CodeWindow, { + const createdWindow = window = this.instantiationService.createInstance(CodeWindow, { state, extensionDevelopmentPath: configuration.extensionDevelopmentPath, isExtensionTestHost: !!configuration.extensionTestsPath @@ -1403,17 +1403,16 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } // Add to our list of windows - WindowsManager.WINDOWS.push(window); + WindowsMainService.WINDOWS.push(window); // Indicate number change via event - this._onWindowsCountChanged.fire({ oldCount: WindowsManager.WINDOWS.length - 1, newCount: WindowsManager.WINDOWS.length }); + this._onWindowsCountChanged.fire({ oldCount: WindowsMainService.WINDOWS.length - 1, newCount: WindowsMainService.WINDOWS.length }); // Window Events + once(window.onClose)(() => this.onWindowClosed(createdWindow)); + once(window.onDestroy)(() => this.onBeforeWindowClose(createdWindow)); // try to save state before destroy because close will not fire window.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own - window.win.webContents.on('devtools-reload-page', () => this.reload(window!)); - window.win.webContents.on('crashed', () => this.onWindowError(window!, WindowError.CRASHED)); - window.win.on('unresponsive', () => this.onWindowError(window!, WindowError.UNRESPONSIVE)); - window.win.on('closed', () => this.onWindowClosed(window!)); + window.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); // Lifecycle (this.lifecycleMainService as LifecycleMainService).registerWindow(window); @@ -1467,9 +1466,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // Load it window.load(configuration); - - // Signal event - this._onWindowLoad.fire(window.id); } private getNewWindowState(configuration: IWindowConfiguration): INewWindowState { @@ -1587,14 +1583,14 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } private ensureNoOverlap(state: ISingleWindowState): ISingleWindowState { - if (WindowsManager.WINDOWS.length === 0) { + if (WindowsMainService.WINDOWS.length === 0) { return state; } state.x = typeof state.x === 'number' ? state.x : 0; state.y = typeof state.y === 'number' ? state.y : 0; - const existingWindowBounds = WindowsManager.WINDOWS.map(win => win.getBounds()); + const existingWindowBounds = WindowsMainService.WINDOWS.map(win => win.getBounds()); while (existingWindowBounds.some(b => b.x === state.x || b.y === state.y)) { state.x += 30; state.y += 30; @@ -1603,23 +1599,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService { return state; } - async reload(win: ICodeWindow, cli?: ParsedArgs): Promise { - - // Only reload when the window has not vetoed this - const veto = await this.lifecycleMainService.unload(win, UnloadReason.RELOAD); - if (!veto) { - win.reload(undefined, cli); - } - } - - closeWorkspace(win: ICodeWindow): void { - this.openInBrowserWindow({ - cli: this.environmentService.args, - windowToUse: win, - remoteAuthority: win.remoteAuthority - }); - } - focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow { const lastActive = this.getLastActiveWindow(); if (lastActive) { @@ -1633,40 +1612,11 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } getLastActiveWindow(): ICodeWindow | undefined { - return getLastActiveWindow(WindowsManager.WINDOWS); + return getLastActiveWindow(WindowsMainService.WINDOWS); } private getLastActiveWindowForAuthority(remoteAuthority: string | undefined): ICodeWindow | undefined { - return getLastActiveWindow(WindowsManager.WINDOWS.filter(window => window.remoteAuthority === remoteAuthority)); - } - - openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[] { - let cli = this.environmentService.args; - const remote = options && options.remoteAuthority; - if (cli && (cli.remote !== remote)) { - cli = { ...cli, remote }; - } - - const forceReuseWindow = options && options.forceReuseWindow; - const forceNewWindow = !forceReuseWindow; - - return this.open({ context, cli, forceEmpty: true, forceNewWindow, forceReuseWindow }); - } - - waitForWindowCloseOrLoad(windowId: number): Promise { - return new Promise(resolve => { - function handler(id: number) { - if (id === windowId) { - closeListener.dispose(); - loadListener.dispose(); - - resolve(); - } - } - - const closeListener = this.onWindowClose(id => handler(id)); - const loadListener = this.onWindowLoad(id => handler(id)); - }); + return getLastActiveWindow(WindowsMainService.WINDOWS.filter(window => window.remoteAuthority === remoteAuthority)); } sendToFocused(channel: string, ...args: any[]): void { @@ -1678,7 +1628,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } sendToAll(channel: string, payload?: any, windowIdsToIgnore?: number[]): void { - for (const window of WindowsManager.WINDOWS) { + for (const window of WindowsMainService.WINDOWS) { if (windowIdsToIgnore && windowIdsToIgnore.indexOf(window.id) >= 0) { continue; // do not send if we are instructed to ignore it } @@ -1697,187 +1647,27 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } getWindowById(windowId: number): ICodeWindow | undefined { - const res = WindowsManager.WINDOWS.filter(window => window.id === windowId); + const res = WindowsMainService.WINDOWS.filter(window => window.id === windowId); + return arrays.firstOrDefault(res); } getWindows(): ICodeWindow[] { - return WindowsManager.WINDOWS; + return WindowsMainService.WINDOWS; } getWindowCount(): number { - return WindowsManager.WINDOWS.length; - } - - private onWindowError(window: ICodeWindow, error: WindowError): void { - this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); - type WindowErrorClassification = { - type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - }; - type WindowErrorEvent = { - type: WindowError; - }; - this.telemetryService.publicLog2('windowerror', { type: error }); - // Unresponsive - if (error === WindowError.UNRESPONSIVE) { - if (window.isExtensionDevelopmentHost || window.isExtensionTestHost || (window.win && window.win.webContents && window.win.webContents.isDevToolsOpened())) { - // TODO@Ben Workaround for https://github.com/Microsoft/vscode/issues/56994 - // In certain cases the window can report unresponsiveness because a breakpoint was hit - // and the process is stopped executing. The most typical cases are: - // - devtools are opened and debugging happens - // - window is an extensions development host that is being debugged - // - window is an extension test development host that is being debugged - return; - } - - // Show Dialog - this.dialogMainService.showMessageBox({ - title: product.nameLong, - type: 'warning', - buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'wait', comment: ['&& denotes a mnemonic'] }, "&&Keep Waiting")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], - message: localize('appStalled', "The window is no longer responding"), - detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), - noLink: true - }, window.win).then(result => { - if (!window.win) { - return; // Return early if the window has been going down already - } - - if (result.response === 0) { - window.reload(); - } else if (result.response === 2) { - this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually - window.win.destroy(); // make sure to destroy the window as it is unresponsive - } - }); - } - - // Crashed - else { - this.dialogMainService.showMessageBox({ - title: product.nameLong, - type: 'warning', - buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], - message: localize('appCrashed', "The window has crashed"), - detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), - noLink: true - }, window.win).then(result => { - if (!window.win) { - return; // Return early if the window has been going down already - } - - if (result.response === 0) { - window.reload(); - } else if (result.response === 1) { - this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually - window.win.destroy(); // make sure to destroy the window as it has crashed - } - }); - } + return WindowsMainService.WINDOWS.length; } private onWindowClosed(win: ICodeWindow): void { - // Tell window - win.dispose(); - // Remove from our list so that Electron can clean it up - const index = WindowsManager.WINDOWS.indexOf(win); - WindowsManager.WINDOWS.splice(index, 1); + const index = WindowsMainService.WINDOWS.indexOf(win); + WindowsMainService.WINDOWS.splice(index, 1); // Emit - this._onWindowsCountChanged.fire({ oldCount: WindowsManager.WINDOWS.length + 1, newCount: WindowsManager.WINDOWS.length }); + this._onWindowsCountChanged.fire({ oldCount: WindowsMainService.WINDOWS.length + 1, newCount: WindowsMainService.WINDOWS.length }); this._onWindowClose.fire(win.id); } - - async pickFileFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { - const paths = await this.dialogMainService.pickFileFolder(options); - if (paths) { - this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFileFolder', options.telemetryExtraData); - const urisToOpen = await Promise.all(paths.map(async path => { - const isDir = await dirExists(path); - - return isDir ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) }; - })); - this.open({ - context: OpenContext.DIALOG, - contextWindowId: win ? win.id : undefined, - cli: this.environmentService.args, - urisToOpen, - forceNewWindow: options.forceNewWindow - }); - } - } - - async pickFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { - const paths = await this.dialogMainService.pickFolder(options); - if (paths) { - this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFolder', options.telemetryExtraData); - this.open({ - context: OpenContext.DIALOG, - contextWindowId: win ? win.id : undefined, - cli: this.environmentService.args, - urisToOpen: paths.map(path => ({ folderUri: URI.file(path) })), - forceNewWindow: options.forceNewWindow - }); - } - } - - async pickFileAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { - const paths = await this.dialogMainService.pickFile(options); - if (paths) { - this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFile', options.telemetryExtraData); - this.open({ - context: OpenContext.DIALOG, - contextWindowId: win ? win.id : undefined, - cli: this.environmentService.args, - urisToOpen: paths.map(path => ({ fileUri: URI.file(path) })), - forceNewWindow: options.forceNewWindow - }); - } - } - - async pickWorkspaceAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { - const paths = await this.dialogMainService.pickWorkspace(options); - if (paths) { - this.sendPickerTelemetry(paths, options.telemetryEventName || 'openWorkspace', options.telemetryExtraData); - this.open({ - context: OpenContext.DIALOG, - contextWindowId: win ? win.id : undefined, - cli: this.environmentService.args, - urisToOpen: paths.map(path => ({ workspaceUri: URI.file(path) })), - forceNewWindow: options.forceNewWindow - }); - } - - } - - private sendPickerTelemetry(paths: string[], telemetryEventName: string, telemetryExtraData?: ITelemetryData) { - const numberOfPaths = paths ? paths.length : 0; - - // Telemetry - // __GDPR__TODO__ Dynamic event names and dynamic properties. Can not be registered statically. - this.telemetryService.publicLog(telemetryEventName, { - ...telemetryExtraData, - outcome: numberOfPaths ? 'success' : 'canceled', - numberOfPaths - }); - } - - quit(): void { - - // If the user selected to exit from an extension development host window, do not quit, but just - // close the window unless this is the last window that is opened. - const window = this.getFocusedWindow(); - if (window && window.isExtensionDevelopmentHost && this.getWindowCount() > 1) { - window.win.close(); - } - - // Otherwise: normal quit - else { - setTimeout(() => { - this.lifecycleMainService.quit(); - }, 10 /* delay to unwind callback stack (IPC) */); - } - } } diff --git a/src/vs/code/electron-main/windowsStateStorage.ts b/src/vs/platform/windows/electron-main/windowsStateStorage.ts similarity index 97% rename from src/vs/code/electron-main/windowsStateStorage.ts rename to src/vs/platform/windows/electron-main/windowsStateStorage.ts index 176f67d8749..165333950bc 100644 --- a/src/vs/code/electron-main/windowsStateStorage.ts +++ b/src/vs/platform/windows/electron-main/windowsStateStorage.ts @@ -5,7 +5,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IWindowState as IWindowUIState } from 'vs/platform/windows/electron-main/windows'; -import { IWindowState, IWindowsState } from 'vs/code/electron-main/windows'; +import { IWindowState, IWindowsState } from 'vs/platform/windows/electron-main/windowsMainService'; export type WindowsStateStorageData = object; diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 974a8c87527..3c09c3a7259 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -294,12 +294,7 @@ function doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace { export function useSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolean { if (isWindows) { - for (const folder of storedFolders) { - if (isRawFileWorkspaceFolder(folder) && folder.path.indexOf(SLASH) >= 0) { - return true; - } - } - return false; + return storedFolders.some(folder => isRawFileWorkspaceFolder(folder) && folder.path.indexOf(SLASH) >= 0); } return true; } diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index eb861920576..35017025a1c 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -265,6 +265,9 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain } const result = this.doEnterWorkspace(window, getWorkspaceIdentifier(path)); + if (!result) { + return null; + } // Emit as event this._onWorkspaceEntered.fire({ window, workspace: result.workspace }); @@ -300,7 +303,11 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return true; // OK } - private doEnterWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult { + private doEnterWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult | null { + if (!window.config) { + return null; + } + window.focus(); // Register window for backups and migrate current backups over diff --git a/src/vs/platform/workspaces/electron-main/workspacesService.ts b/src/vs/platform/workspaces/electron-main/workspacesService.ts index 41f4cb982c2..24a9bfbcb18 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesService.ts @@ -52,7 +52,7 @@ export class WorkspacesService implements AddFirstParameterToFunctions { const window = this.windowsMainService.getWindowById(windowId); - if (window) { + if (window && window.config) { return this.workspacesHistoryMainService.getRecentlyOpened(window.config.workspace, window.config.folderUri, window.config.filesToOpenOrCreate); } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 8ea0f29d743..b60d66b6b4f 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5222,7 +5222,7 @@ declare module 'vscode' { /** * The shell command line. Is `undefined` if created with a command and arguments. */ - commandLine: string; + commandLine: string | undefined; /** * The shell command. Is `undefined` if created with a full command line. @@ -7016,6 +7016,7 @@ declare module 'vscode' { /** * An optional human-readable message that will be rendered in the view. + * Setting the message to null, undefined, or empty string will remove the message from the view. */ message?: string; @@ -7297,7 +7298,7 @@ declare module 'vscode' { * A number can be used to provide an exit code for the terminal. Exit codes must be * positive and a non-zero exit codes signals failure which shows a notification for a * regular terminal and allows dependent tasks to proceed when used with the - * `CustomExecution2` API. + * `CustomExecution` API. * * **Example:** Exit the terminal when "y" is pressed, otherwise show a notification. * ```typescript diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 0f113996abc..b4ab5f9a664 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -59,15 +59,15 @@ declare module 'vscode' { } export class CallHierarchyIncomingCall { - source: CallHierarchyItem; - sourceRanges: Range[]; - constructor(item: CallHierarchyItem, sourceRanges: Range[]); + from: CallHierarchyItem; + fromRanges: Range[]; + constructor(item: CallHierarchyItem, fromRanges: Range[]); } export class CallHierarchyOutgoingCall { - sourceRanges: Range[]; - target: CallHierarchyItem; - constructor(item: CallHierarchyItem, sourceRanges: Range[]); + fromRanges: Range[]; + to: CallHierarchyItem; + constructor(item: CallHierarchyItem, fromRanges: Range[]); } export interface CallHierarchyItemProvider { @@ -764,6 +764,35 @@ declare module 'vscode' { //#endregion + //#region Joao: SCM tree rendering + + /** + * Options for creating a [SourceControl](#SourceControl) instance. + */ + export interface SourceControlOptions { + + /** + * Whether tree rendering is supported by the [SourceControl](#SourceControl) instance. + */ + readonly treeRendering?: boolean; + } + + export namespace scm { + + /** + * Creates a new [source control](#SourceControl) instance. + * + * @param id An `id` for the source control. Something short, e.g.: `git`. + * @param label A human-readable string for the source control. E.g.: `Git`. + * @param rootUri An optional Uri of the root of the source control. E.g.: `Uri.parse(workspaceRoot)`. + * @param options Additional options for creating the source control. + * @return An instance of [source control](#SourceControl). + */ + export function createSourceControl(id: string, label: string, rootUri?: Uri, options?: SourceControlOptions): SourceControl; + } + + //#endregion + //#region Joao: SCM Input Box /** @@ -871,7 +900,8 @@ declare module 'vscode' { export interface TreeView { /** - * The name of the tree view. It is set from the extension package.json and can be changed later. + * The tree view title is initially taken from the extension package.json + * Changes to the title property will be properly reflected in the UI in the title of the view. */ title?: string; } @@ -912,7 +942,7 @@ declare module 'vscode' { /** * Class used to execute an extension callback as a task. */ - export class CustomExecution2 { + export class CustomExecution { /** * Constructs a CustomExecution task object. The callback will be executed the task is run, at which point the * extension should return the Pseudoterminal it will "run in". The task should wait to do further execution until @@ -941,12 +971,12 @@ declare module 'vscode' { * or '$eslint'. Problem matchers can be contributed by an extension using * the `problemMatchers` extension point. */ - constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]); + constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); /** * The task's execution engine */ - execution2?: ProcessExecution | ShellExecution | CustomExecution2; + execution2?: ProcessExecution | ShellExecution | CustomExecution; } //#endregion @@ -1085,45 +1115,7 @@ declare module 'vscode' { //#region Custom editors, mjbvz - export enum WebviewContentState { - /** - * The webview content cannot be modified. - * - * This disables save. - */ - Readonly = 1, - - /** - * The webview content has not been changed but they can be modified and saved. - */ - Unchanged = 2, - - /** - * The webview content has been changed and can be saved. - */ - Dirty = 3, - } - - export interface WebviewEditorState { - readonly contentState: WebviewContentState; - } - - export interface WebviewPanel { - editorState: WebviewEditorState; - - /** - * Fired when the webview is being saved. - * - * Both `Unchanged` and `Dirty` editors can be saved. - * - * Extensions should call `waitUntil` to signal when the save operation complete - */ - readonly onWillSave: Event<{ waitUntil: (thenable: Thenable) => void }>; - } - export interface WebviewEditor extends WebviewPanel { - // TODO: We likely do not want `editorState` and `onWillSave` enabled for - // resource backed webviews } export interface WebviewEditorProvider { @@ -1147,28 +1139,30 @@ declare module 'vscode' { //#endregion - // #region resolveExternalUri — mjbvz + // #region asExternalUri — mjbvz namespace env { /** * Resolves an *external* uri, such as a `http:` or `https:` link, from where the extension is running to a * uri to the same resource on the client machine. * - * This is a no-op if the extension is running locally. Currently only supports `https:` and `http:`. + * This is a no-op if the extension is running on the client machine. Currently only supports + * `https:` and `http:` uris. * - * If the extension is running remotely, this function automatically establishes port forwarding from - * the local machine to `target` on the remote and returns a local uri that can be used to for this connection. + * If the extension is running remotely, this function automatically establishes a port forwarding tunnel + * from the local machine to `target` on the remote and returns a local uri to the tunnel. The lifetime of + * the port fowarding tunnel is managed by VS Code and the tunnel can be closed by the user. * - * Extensions should not store the result of `resolveExternalUri` as the resolved uri may become invalid due to - * a system or user action — for example, in remote cases, a user may close a port that was forwarded by - * `resolveExternalUri`. + * Extensions should not cache the result of `asExternalUri` as the resolved uri may become invalid due to + * a system or user action — for example, in remote cases, a user may close a port forwardng tunnel + * that was opened by `asExternalUri`. * - * Note: uris passed through `openExternal` are automatically resolved and you should not call `resolveExternalUri` + * Note: uris passed through `openExternal` are automatically resolved and you should not call `asExternalUri` * on them. * * @return A uri that can be used on the client machine. */ - export function resolveExternalUri(target: Uri): Thenable; + export function asExternalUri(target: Uri): Thenable; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index cefcf818f89..b504c55e3b2 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -12,6 +12,7 @@ import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/br import { DisposableStore } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { isEqual } from 'vs/base/common/resources'; // todo@joh move these things back into something like contrib/insets class EditorWebviewZone implements IViewZone { @@ -75,7 +76,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { id = id.substr(0, id.indexOf(',')); //todo@joh HACK for (const candidate of this._editorService.listCodeEditors()) { - if (candidate.getId() === id && candidate.hasModel() && candidate.getModel()!.uri.toString() === URI.revive(uri).toString()) { + if (candidate.getId() === id && candidate.hasModel() && isEqual(candidate.getModel().uri, URI.revive(uri))) { editor = candidate; break; } diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index 520801babe6..239f87096f1 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -16,6 +16,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2 import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType } from 'vs/workbench/api/common/extHost.protocol'; import { IEditor } from 'vs/workbench/common/editor'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { equals } from 'vs/base/common/arrays'; export interface IFocusTracker { onGainedFocus(): void; @@ -124,28 +125,12 @@ export class MainThreadTextEditorProperties { return null; } - private static _selectionsEqual(a: Selection[], b: Selection[]): boolean { - if (a.length !== b.length) { - return false; - } - for (let i = 0; i < a.length; i++) { - if (!a[i].equalsSelection(b[i])) { - return false; - } - } - return true; + private static _selectionsEqual(a: readonly Selection[], b: readonly Selection[]): boolean { + return equals(a, b, (aValue, bValue) => aValue.equalsSelection(bValue)); } - private static _rangesEqual(a: Range[], b: Range[]): boolean { - if (a.length !== b.length) { - return false; - } - for (let i = 0; i < a.length; i++) { - if (!a[i].equalsRange(b[i])) { - return false; - } - } - return true; + private static _rangesEqual(a: readonly Range[], b: readonly Range[]): boolean { + return equals(a, b, (aValue, bValue) => aValue.equalsRange(bValue)); } private static _optionsEqual(a: IResolvedTextEditorConfiguration, b: IResolvedTextEditorConfiguration): boolean { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 0ea4d540b11..05bc4303f3e 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -499,10 +499,10 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha if (!outgoing) { return outgoing; } - return outgoing.map(([item, sourceRanges]): callh.OutgoingCall => { + return outgoing.map(([item, fromRanges]): callh.OutgoingCall => { return { - target: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item), - sourceRanges + to: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item), + fromRanges }; }); }, @@ -511,10 +511,10 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha if (!incoming) { return incoming; } - return incoming.map(([item, sourceRanges]): callh.IncomingCall => { + return incoming.map(([item, fromRanges]): callh.IncomingCall => { return { - source: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item), - sourceRanges + from: MainThreadLanguageFeatures._reviveCallHierarchyItemDto(item), + fromRanges }; }); } diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 4488720cf9a..9e9f9ff7dbf 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/contrib/scm/common/scm'; -import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol'; +import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMProviderProps, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol'; import { Command } from 'vs/editor/common/modes'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ISplice, Sequence } from 'vs/base/common/sequence'; @@ -128,12 +128,15 @@ class MainThreadSCMProvider implements ISCMProvider { private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; + get treeRendering(): boolean { return this._props.treeRendering; } + constructor( private readonly proxy: ExtHostSCMShape, private readonly _handle: number, private readonly _contextValue: string, private readonly _label: string, private readonly _rootUri: URI | undefined, + private readonly _props: SCMProviderProps, @ISCMService scmService: ISCMService ) { } @@ -287,8 +290,8 @@ export class MainThreadSCM implements MainThreadSCMShape { this._disposables.dispose(); } - $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void { - const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri && URI.revive(rootUri), this.scmService); + $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined, props: SCMProviderProps): void { + const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri && URI.revive(rootUri), props, this.scmService); const repository = this.scmService.registerSCMProvider(provider); this._repositories.set(handle, repository); diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index c849ba99630..9bf1db3fe1c 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -50,7 +50,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant { // Nothing } - async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO); } @@ -112,7 +112,7 @@ export class FinalNewLineParticipant implements ISaveParticipantParticipant { // Nothing } - async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doInsertFinalNewLine(model.textEditorModel); } @@ -146,7 +146,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant // Nothing } - async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO); } @@ -216,7 +216,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { // Nothing } - async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { const model = editorModel.textEditorModel; const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }; @@ -250,7 +250,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant { @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { } - async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { if (env.reason === SaveReason.AUTO) { return undefined; } @@ -333,7 +333,7 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentSaveParticipant); } - async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { if (!shouldSynchronizeModel(editorModel.textEditorModel)) { // the model never made it to the extension @@ -344,10 +344,8 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant { return new Promise((resolve, reject) => { setTimeout(() => reject(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms")), 1750); this._proxy.$participateInSave(editorModel.getResource(), env.reason).then(values => { - for (const success of values) { - if (!success) { - return Promise.reject(new Error('listener failed')); - } + if (!values.every(success => success)) { + return Promise.reject(new Error('listener failed')); } return undefined; }).then(resolve, reject); @@ -384,7 +382,7 @@ export class SaveParticipant implements ISaveParticipant { this._saveParticipants.dispose(); } - async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { return this._progressService.withProgress({ location: ProgressLocation.Window }, progress => { progress.report({ message: localize('saveParticipants', "Running Save Participants...") }); const promiseFactory = this._saveParticipants.getValue().map(p => () => { diff --git a/src/vs/workbench/api/browser/mainThreadStatusBar.ts b/src/vs/workbench/api/browser/mainThreadStatusBar.ts index 611538d554f..91a9ba1419f 100644 --- a/src/vs/workbench/api/browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/browser/mainThreadStatusBar.ts @@ -24,9 +24,13 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { this.entries.clear(); } - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void { + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: string | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined): void { const entry: IStatusbarEntry = { text, tooltip, command, color }; + if (typeof priority === 'undefined') { + priority = 0; + } + // Reset existing entry if alignment or priority changed let existingEntry = this.entries.get(id); if (existingEntry && (existingEntry.alignment !== alignment || existingEntry.priority !== priority)) { diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 9e5763d0614..e8b10b69e5d 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -29,7 +29,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO, - ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecution2DTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, + ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, RunOptionsDTO } from 'vs/workbench/api/common/shared/tasks'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; @@ -131,7 +131,7 @@ namespace ProcessExecutionOptionsDTO { } namespace ProcessExecutionDTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is ProcessExecutionDTO { + export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ProcessExecutionDTO { const candidate = value as ProcessExecutionDTO; return candidate && !!candidate.process; } @@ -199,7 +199,7 @@ namespace ShellExecutionOptionsDTO { } namespace ShellExecutionDTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is ShellExecutionDTO { + export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ShellExecutionDTO { const candidate = value as ShellExecutionDTO; return candidate && (!!candidate.commandLine || !!candidate.command); } @@ -230,21 +230,21 @@ namespace ShellExecutionDTO { } } -namespace CustomExecution2DTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is CustomExecution2DTO { - const candidate = value as CustomExecution2DTO; - return candidate && candidate.customExecution === 'customExecution2'; +namespace CustomExecutionDTO { + export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is CustomExecutionDTO { + const candidate = value as CustomExecutionDTO; + return candidate && candidate.customExecution === 'customExecution'; } - export function from(value: CommandConfiguration): CustomExecution2DTO { + export function from(value: CommandConfiguration): CustomExecutionDTO { return { - customExecution: 'customExecution2' + customExecution: 'customExecution' }; } - export function to(value: CustomExecution2DTO): CommandConfiguration { + export function to(value: CustomExecutionDTO): CommandConfiguration { return { - runtime: RuntimeType.CustomExecution2, + runtime: RuntimeType.CustomExecution, presentation: undefined }; } @@ -264,7 +264,7 @@ namespace TaskSourceDTO { } } else if (value.kind === TaskSourceKind.Workspace) { result.extensionId = '$core'; - result.scope = value.config.workspaceFolder.uri; + result.scope = value.config.workspaceFolder ? value.config.workspaceFolder.uri : TaskScope.Global; } return result; } @@ -351,8 +351,8 @@ namespace TaskDTO { command = ShellExecutionDTO.to(task.execution); } else if (ProcessExecutionDTO.is(task.execution)) { command = ProcessExecutionDTO.to(task.execution); - } else if (CustomExecution2DTO.is(task.execution)) { - command = CustomExecution2DTO.to(task.execution); + } else if (CustomExecutionDTO.is(task.execution)) { + command = CustomExecutionDTO.to(task.execution); } } diff --git a/src/vs/workbench/api/browser/mainThreadUrls.ts b/src/vs/workbench/api/browser/mainThreadUrls.ts index c5f54274e72..a89f291d6c2 100644 --- a/src/vs/workbench/api/browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/browser/mainThreadUrls.ts @@ -5,7 +5,7 @@ import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from '../common/extHostCustomers'; -import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; +import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/common/url'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IExtensionUrlHandler } from 'vs/workbench/services/extensions/browser/extensionUrlHandler'; @@ -19,7 +19,7 @@ class ExtensionUrlHandler implements IURLHandler { readonly extensionId: ExtensionIdentifier ) { } - handleURL(uri: URI): Promise { + handleURL(uri: URI, options?: IOpenURLOptions): Promise { if (!ExtensionIdentifier.equals(this.extensionId, uri.authority)) { return Promise.resolve(false); } diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 207809e570f..1c0153b7331 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -20,7 +20,7 @@ import { IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import { ICreateWebViewShowOptions, WebviewInputOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -80,7 +80,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews @IExtensionService extensionService: IExtensionService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, - @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService, + @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @IOpenerService private readonly _openerService: IOpenerService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IProductService private readonly _productService: IProductService, @@ -93,9 +93,10 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews // This reviver's only job is to activate webview panel extensions // This should trigger the real reviver to be registered from the extension host side. - this._register(_webviewEditorService.registerResolver({ + this._register(_webviewWorkbenchService.registerResolver({ canResolve: (webview: WebviewInput) => { if (webview.getTypeId() === CustomFileEditorInput.typeId) { + extensionService.activateByEvent(`onWebviewEditor:${(webview as CustomFileEditorInput).viewType}`); return false; } @@ -124,7 +125,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn); } - const webview = this._webviewEditorService.createWebview(handle, this.getInternalWebviewViewType(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), { + const webview = this._webviewWorkbenchService.createWebview(handle, this.getInternalWebviewViewType(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), { location: URI.revive(extensionLocation), id: extensionId }); @@ -185,7 +186,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0); if (targetGroup) { - this._webviewEditorService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus); + this._webviewWorkbenchService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus); } } @@ -200,7 +201,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews throw new Error(`Reviver for ${viewType} already registered`); } - this._revivers.set(viewType, this._webviewEditorService.registerResolver({ + this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({ canResolve: (webviewEditorInput) => { return webviewEditorInput.viewType === this.getInternalWebviewViewType(viewType); }, @@ -249,7 +250,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews throw new Error(`Provider for ${viewType} already registered`); } - this._editorProviders.set(viewType, this._webviewEditorService.registerResolver({ + this._editorProviders.set(viewType, this._webviewWorkbenchService.registerResolver({ canResolve: (webviewEditorInput) => { return webviewEditorInput.getTypeId() !== WebviewInput.typeId && webviewEditorInput.viewType === viewType; }, @@ -339,8 +340,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews const handle = this._webviewEditorInputs.getHandleForInput(editorInput); if (handle) { viewStates[handle] = { - visible: topLevelInput.matches(group.activeEditor), - active: topLevelInput.matches(activeInput), + visible: topLevelInput === group.activeEditor, + active: topLevelInput === activeInput, position: editorGroupToViewColumn(this._editorGroupService, group.id), }; } diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index ff2074055e4..bbce19a3388 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -47,7 +47,7 @@ export class MainThreadWindow implements MainThreadWindowShape { return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling }); } - async $resolveExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { + async $asExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { const uri = URI.revive(uriComponents); const result = await this.openerService.resolveExternalUri(uri, options); return result.resolved; diff --git a/src/vs/workbench/api/browser/media/test.svg b/src/vs/workbench/api/browser/media/test.svg index 57cd408942d..569947a033e 100644 --- a/src/vs/workbench/api/browser/media/test.svg +++ b/src/vs/workbench/api/browser/media/test.svg @@ -1,10 +1,3 @@ - - - - - - - - + + - diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index a1f752a77a0..01581c2c7c2 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -39,7 +39,7 @@ interface IOpenFolderAPICommandOptions { } export class OpenFolderAPICommand { - public static ID = 'vscode.openFolder'; + public static readonly ID = 'vscode.openFolder'; public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise; public static execute(executor: ICommandsExecutor, uri?: URI, options?: IOpenFolderAPICommandOptions): Promise; public static execute(executor: ICommandsExecutor, uri?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}): Promise { @@ -73,7 +73,7 @@ interface INewWindowAPICommandOptions { } export class NewWindowAPICommand { - public static ID = 'vscode.newWindow'; + public static readonly ID = 'vscode.newWindow'; public static execute(executor: ICommandsExecutor, options?: INewWindowAPICommandOptions): Promise { const commandOptions: IOpenEmptyWindowOptions = { forceReuseWindow: options && options.reuseWindow, @@ -94,7 +94,7 @@ CommandsRegistry.registerCommand({ }); export class DiffAPICommand { - public static ID = 'vscode.diff'; + public static readonly ID = 'vscode.diff'; public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions): Promise { return executor.executeCommand('_workbench.diff', [ left, right, @@ -108,7 +108,7 @@ export class DiffAPICommand { CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute)); export class OpenAPICommand { - public static ID = 'vscode.open'; + public static readonly ID = 'vscode.open'; public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Promise { let options: ITextEditorOptions | undefined; let position: EditorViewColumn | undefined; @@ -138,7 +138,7 @@ CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function }); export class RemoveFromRecentlyOpenedAPICommand { - public static ID = 'vscode.removeFromRecentlyOpened'; + public static readonly ID = 'vscode.removeFromRecentlyOpened'; public static execute(executor: ICommandsExecutor, path: string | URI): Promise { if (typeof path === 'string') { path = path.match(/^[^:/?#]+:\/\//) ? URI.parse(path) : URI.file(path); @@ -151,7 +151,7 @@ export class RemoveFromRecentlyOpenedAPICommand { CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute)); export class OpenIssueReporter { - public static ID = 'vscode.openIssueReporter'; + public static readonly ID = 'vscode.openIssueReporter'; public static execute(executor: ICommandsExecutor, extensionId: string): Promise { return executor.executeCommand('workbench.action.openIssueReporter', [extensionId]); } @@ -185,7 +185,7 @@ CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function }); export class SetEditorLayoutAPICommand { - public static ID = 'vscode.setEditorLayout'; + public static readonly ID = 'vscode.setEditorLayout'; public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise { return executor.executeCommand('layoutEditorGroups', layout); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 5a6567b8a79..98f5405717a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -247,9 +247,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I openExternal(uri: URI) { return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote }); }, - resolveExternalUri(uri: URI) { + asExternalUri(uri: URI) { checkProposedApiEnabled(extension); - return extHostWindow.resolveExternalUri(uri, { allowTunneling: !!initData.remote.isRemote }); + return extHostWindow.asExternalUri(uri, { allowTunneling: !!initData.remote.isRemote }); }, get remoteName() { return getRemoteName(initData.remote.authority); @@ -630,7 +630,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const options = uriOrFileNameOrOptions as { language?: string; content?: string; }; if (typeof uriOrFileNameOrOptions === 'string') { uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions)); - } else if (uriOrFileNameOrOptions instanceof URI) { + } else if (URI.isUri(uriOrFileNameOrOptions)) { uriPromise = Promise.resolve(uriOrFileNameOrOptions); } else if (!options || typeof options === 'object') { uriPromise = extHostDocuments.createDocumentData(options); @@ -709,8 +709,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get inputBox() { return extHostSCM.getLastInputBox(extension)!; // Strict null override - Deprecated api }, - createSourceControl(id: string, label: string, rootUri?: vscode.Uri) { - return extHostSCM.createSourceControl(extension, id, label, rootUri); + createSourceControl(id: string, label: string, rootUri?: vscode.Uri, opts?: vscode.SourceControlOptions) { + return extHostSCM.createSourceControl(extension, id, label, rootUri, opts); } }; @@ -847,7 +847,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I EndOfLine: extHostTypes.EndOfLine, EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, - CustomExecution2: extHostTypes.CustomExecution2, + CustomExecution: extHostTypes.CustomExecution, FileChangeType: extHostTypes.FileChangeType, FileSystemError: extHostTypes.FileSystemError, FileType: files.FileType, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 10f4c36261d..ca01c32fdb6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -505,7 +505,7 @@ export interface MainThreadQuickOpenShape extends IDisposable { } export interface MainThreadStatusBarShape extends IDisposable { - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: statusbar.StatusbarAlignment, priority: number | undefined): void; + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: string | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined): void; $dispose(id: number): void; } @@ -656,6 +656,10 @@ export interface MainThreadExtensionServiceShape extends IDisposable { $onExtensionHostExit(code: number): void; } +export interface SCMProviderProps { + readonly treeRendering: boolean; +} + export interface SCMProviderFeatures { hasQuickDiffProvider?: boolean; count?: number; @@ -689,7 +693,7 @@ export type SCMRawResourceSplices = [ ]; export interface MainThreadSCMShape extends IDisposable { - $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void; + $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined, props: SCMProviderProps): void; $updateSourceControl(handle: number, features: SCMProviderFeatures): void; $unregisterSourceControl(handle: number): void; @@ -746,7 +750,7 @@ export interface IOpenUriOptions { export interface MainThreadWindowShape extends IDisposable { $getWindowVisibility(): Promise; $openUri(uri: UriComponents, options: IOpenUriOptions): Promise; - $resolveExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; + $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; } // -- extension host @@ -1189,7 +1193,7 @@ export interface ExtHostTerminalServiceShape { $acceptTerminalTitleChange(id: number, name: string): void; $acceptTerminalDimensions(id: number, cols: number, rows: number): void; $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void; - $spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; + $spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): void; $acceptProcessInput(id: number, data: string): void; $acceptProcessResize(id: number, cols: number, rows: number): void; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index e9022a259d5..ebe9352c103 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as types from 'vs/workbench/api/common/extHostTypes'; @@ -27,7 +27,7 @@ export class ExtHostApiCommands { } private _commands: ExtHostCommands; - private _disposables: IDisposable[] = []; + private readonly _disposables = new DisposableStore(); private constructor(commands: ExtHostCommands) { this._commands = commands; @@ -239,7 +239,7 @@ export class ExtHostApiCommands { this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), { description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', args: [ - { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI }, + { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || URI.isUri(value) }, { name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Whether the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' } ] }); @@ -288,7 +288,7 @@ export class ExtHostApiCommands { private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void { const disposable = this._commands.registerCommand(false, id, handler, this, description); - this._disposables.push(disposable); + this._disposables.add(disposable); } /** @@ -576,30 +576,30 @@ export class ExtHostApiCommands { private async _executeCallHierarchyIncomingCallsProvider(resource: URI, position: types.Position): Promise { type IncomingCallDto = { - source: ICallHierarchyItemDto; - sourceRanges: IRange[]; + from: ICallHierarchyItemDto; + fromRanges: IRange[]; }; const args = { resource, position: typeConverters.Position.from(position) }; const calls = await this._commands.executeCommand('_executeCallHierarchyIncomingCalls', args); const result: vscode.CallHierarchyIncomingCall[] = []; for (const call of calls) { - result.push(new types.CallHierarchyIncomingCall(typeConverters.CallHierarchyItem.to(call.source), call.sourceRanges.map(typeConverters.Range.to))); + result.push(new types.CallHierarchyIncomingCall(typeConverters.CallHierarchyItem.to(call.from), call.fromRanges.map(typeConverters.Range.to))); } return result; } private async _executeCallHierarchyOutgoingCallsProvider(resource: URI, position: types.Position): Promise { type OutgoingCallDto = { - sourceRanges: IRange[]; - target: ICallHierarchyItemDto; + fromRanges: IRange[]; + to: ICallHierarchyItemDto; }; const args = { resource, position: typeConverters.Position.from(position) }; const calls = await this._commands.executeCommand('_executeCallHierarchyOutgoingCalls', args); const result: vscode.CallHierarchyOutgoingCall[] = []; for (const call of calls) { - result.push(new types.CallHierarchyOutgoingCall(typeConverters.CallHierarchyItem.to(call.target), call.sourceRanges.map(typeConverters.Range.to))); + result.push(new types.CallHierarchyOutgoingCall(typeConverters.CallHierarchyItem.to(call.to), call.fromRanges.map(typeConverters.Range.to))); } return result; } diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index d654408346b..1455bb030d8 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -64,7 +64,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._checkDisposed(); let toSync: vscode.Uri[] = []; - if (first instanceof URI) { + if (URI.isUri(first)) { if (!diagnostics) { // remove this entry diff --git a/src/vs/workbench/api/common/extHostDocumentData.ts b/src/vs/workbench/api/common/extHostDocumentData.ts index b44527a2b51..3a166503923 100644 --- a/src/vs/workbench/api/common/extHostDocumentData.ts +++ b/src/vs/workbench/api/common/extHostDocumentData.ts @@ -12,6 +12,7 @@ import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model import { MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol'; import { EndOfLine, Position, Range } from 'vs/workbench/api/common/extHostTypes'; import * as vscode from 'vscode'; +import { equals } from 'vs/base/common/arrays'; const _modeId2WordDefinition = new Map(); export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp | undefined): void { @@ -48,17 +49,8 @@ export class ExtHostDocumentData extends MirrorTextModel { this._isDirty = false; } - equalLines(lines: string[]): boolean { - const len = lines.length; - if (len !== this._lines.length) { - return false; - } - for (let i = 0; i < len; i++) { - if (lines[i] !== this._lines[i]) { - return false; - } - } - return true; + equalLines(lines: readonly string[]): boolean { + return equals(this._lines, lines); } get document(): vscode.TextDocument { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 94636f78be5..df94581ec5d 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1021,7 +1021,7 @@ class CallHierarchyAdapter { if (!calls) { return undefined; } - return calls.map(call => (<[extHostProtocol.ICallHierarchyItemDto, IRange[]]>[typeConvert.CallHierarchyItem.from(call.source), call.sourceRanges.map(typeConvert.Range.from)])); + return calls.map(call => (<[extHostProtocol.ICallHierarchyItemDto, IRange[]]>[typeConvert.CallHierarchyItem.from(call.from), call.fromRanges.map(typeConvert.Range.from)])); } async provideCallsFrom(uri: URI, position: IPosition, token: CancellationToken): Promise<[extHostProtocol.ICallHierarchyItemDto, IRange[]][] | undefined> { @@ -1031,7 +1031,7 @@ class CallHierarchyAdapter { if (!calls) { return undefined; } - return calls.map(call => (<[extHostProtocol.ICallHierarchyItemDto, IRange[]]>[typeConvert.CallHierarchyItem.from(call.target), call.sourceRanges.map(typeConvert.Range.from)])); + return calls.map(call => (<[extHostProtocol.ICallHierarchyItemDto, IRange[]]>[typeConvert.CallHierarchyItem.from(call.to), call.fromRanges.map(typeConvert.Range.from)])); } } diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 3d790f26276..c8587620994 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -455,7 +455,7 @@ function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light function getLightIconUri(iconPath: QuickInputButton['iconPath']) { if (iconPath && !(iconPath instanceof ThemeIcon)) { if (typeof iconPath === 'string' - || iconPath instanceof URI) { + || URI.isUri(iconPath)) { return getIconUri(iconPath); } return getIconUri((iconPath as any).light); @@ -471,7 +471,7 @@ function getDarkIconUri(iconPath: QuickInputButton['iconPath']) { } function getIconUri(iconPath: string | URI) { - if (iconPath instanceof URI) { + if (URI.isUri(iconPath)) { return iconPath; } return URI.file(iconPath); diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 80c0c007f65..47db2513d83 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -9,8 +9,8 @@ import { debounce } from 'vs/base/common/decorators'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { asPromise } from 'vs/base/common/async'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto } from './extHost.protocol'; -import { sortedDiff } from 'vs/base/common/arrays'; +import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, SCMProviderProps } from './extHost.protocol'; +import { sortedDiff, equals } from 'vs/base/common/arrays'; import { comparePaths } from 'vs/base/common/comparers'; import * as vscode from 'vscode'; import { ISplice } from 'vs/base/common/sequence'; @@ -126,18 +126,8 @@ function commandEquals(a: vscode.Command, b: vscode.Command): boolean { && (a.arguments && b.arguments ? compareArgs(a.arguments, b.arguments) : a.arguments === b.arguments); } -function commandListEquals(a: vscode.Command[], b: vscode.Command[]): boolean { - if (a.length !== b.length) { - return false; - } - - for (let i = 0; i < a.length; i++) { - if (!commandEquals(a[i], b[i])) { - return false; - } - } - - return true; +function commandListEquals(a: readonly vscode.Command[], b: readonly vscode.Command[]): boolean { + return equals(a, b, commandEquals); } export interface IValidateInput { @@ -174,9 +164,9 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { this._placeholder = placeholder; } - private _validateInput: IValidateInput; + private _validateInput: IValidateInput | undefined; - get validateInput(): IValidateInput { + get validateInput(): IValidateInput | undefined { if (!this._extension.enableProposedApi) { throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); } @@ -184,7 +174,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { return this._validateInput; } - set validateInput(fn: IValidateInput) { + set validateInput(fn: IValidateInput | undefined) { if (!this._extension.enableProposedApi) { throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); } @@ -462,10 +452,15 @@ class ExtHostSourceControl implements vscode.SourceControl { private _commands: ExtHostCommands, private _id: string, private _label: string, - private _rootUri?: vscode.Uri + private _rootUri: vscode.Uri | undefined, + _props: SCMProviderProps ) { + if (!_extension.enableProposedApi && _props.treeRendering) { + throw new Error(`[${_extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${_extension.identifier.value}`); + } + this._inputBox = new ExtHostSCMInputBox(_extension, this._proxy, this.handle); - this._proxy.$registerSourceControl(this.handle, _id, _label, _rootUri); + this._proxy.$registerSourceControl(this.handle, _id, _label, _rootUri, _props); } private updatedResourceGroups = new Set(); @@ -527,6 +522,12 @@ class ExtHostSourceControl implements vscode.SourceControl { } } +function asProps(options: vscode.SourceControlOptions | undefined): SCMProviderProps { + return { + treeRendering: options && !!options.treeRendering || false + }; +} + export class ExtHostSCM implements ExtHostSCMShape { private static _handlePool: number = 0; @@ -586,11 +587,11 @@ export class ExtHostSCM implements ExtHostSCMShape { }); } - createSourceControl(extension: IExtensionDescription, id: string, label: string, rootUri: vscode.Uri | undefined): vscode.SourceControl { + createSourceControl(extension: IExtensionDescription, id: string, label: string, rootUri: vscode.Uri | undefined, options?: vscode.SourceControlOptions): vscode.SourceControl { this.logService.trace('ExtHostSCM#createSourceControl', extension.identifier.value, id, label, rootUri); const handle = ExtHostSCM._handlePool++; - const sourceControl = new ExtHostSourceControl(extension, this._proxy, this._commands, id, label, rootUri); + const sourceControl = new ExtHostSourceControl(extension, this._proxy, this._commands, id, label, rootUri, asProps(options)); this._sourceControls.set(handle, sourceControl); const sourceControls = this._sourceControlsByExtension.get(ExtensionIdentifier.toKey(extension.identifier)) || []; @@ -667,7 +668,7 @@ export class ExtHostSCM implements ExtHostSCMShape { return Promise.resolve(undefined); } - return asPromise(() => sourceControl.inputBox.validateInput(value, cursorPosition)).then(result => { + return asPromise(() => sourceControl.inputBox.validateInput!(value, cursorPosition)).then(result => { if (!result) { return Promise.resolve(undefined); } diff --git a/src/vs/workbench/api/common/extHostStatusBar.ts b/src/vs/workbench/api/common/extHostStatusBar.ts index d744fdd80d2..02c89a6e89e 100644 --- a/src/vs/workbench/api/common/extHostStatusBar.ts +++ b/src/vs/workbench/api/common/extHostStatusBar.ts @@ -15,16 +15,16 @@ export class ExtHostStatusBarEntry implements StatusBarItem { private _id: number; private _alignment: number; private _priority?: number; - private _disposed: boolean; - private _visible: boolean; + private _disposed: boolean = false; + private _visible: boolean = false; private _statusId: string; private _statusName: string; - private _text: string; - private _tooltip: string; - private _color: string | ThemeColor; - private _command: string; + private _text: string = ''; + private _tooltip?: string; + private _color?: string | ThemeColor; + private _command?: string; private _timeoutHandle: any; private _proxy: MainThreadStatusBarShape; @@ -54,15 +54,15 @@ export class ExtHostStatusBarEntry implements StatusBarItem { return this._text; } - public get tooltip(): string { + public get tooltip(): string | undefined { return this._tooltip; } - public get color(): string | ThemeColor { + public get color(): string | ThemeColor | undefined { return this._color; } - public get command(): string { + public get command(): string | undefined { return this._command; } @@ -71,17 +71,17 @@ export class ExtHostStatusBarEntry implements StatusBarItem { this.update(); } - public set tooltip(tooltip: string) { + public set tooltip(tooltip: string | undefined) { this._tooltip = tooltip; this.update(); } - public set color(color: string | ThemeColor) { + public set color(color: string | ThemeColor | undefined) { this._color = color; this.update(); } - public set command(command: string) { + public set command(command: string | undefined) { this._command = command; this.update(); } diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 7cd590692fd..1fbe711b32b 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -88,7 +88,7 @@ export namespace ProcessExecutionOptionsDTO { } export namespace ProcessExecutionDTO { - export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ProcessExecutionDTO { + export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecutionDTO | undefined): value is tasks.ProcessExecutionDTO { if (value) { const candidate = value as tasks.ProcessExecutionDTO; return candidate && !!candidate.process; @@ -133,7 +133,7 @@ export namespace ShellExecutionOptionsDTO { } export namespace ShellExecutionDTO { - export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ShellExecutionDTO { + export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecutionDTO | undefined): value is tasks.ShellExecutionDTO { if (value) { const candidate = value as tasks.ShellExecutionDTO; return candidate && (!!candidate.commandLine || !!candidate.command); @@ -170,19 +170,19 @@ export namespace ShellExecutionDTO { } } -export namespace CustomExecution2DTO { - export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.CustomExecution2DTO { +export namespace CustomExecutionDTO { + export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecutionDTO | undefined): value is tasks.CustomExecutionDTO { if (value) { - let candidate = value as tasks.CustomExecution2DTO; - return candidate && candidate.customExecution === 'customExecution2'; + let candidate = value as tasks.CustomExecutionDTO; + return candidate && candidate.customExecution === 'customExecution'; } else { return false; } } - export function from(value: vscode.CustomExecution2): tasks.CustomExecution2DTO { + export function from(value: vscode.CustomExecution): tasks.CustomExecutionDTO { return { - customExecution: 'customExecution2' + customExecution: 'customExecution' }; } } @@ -220,13 +220,13 @@ export namespace TaskDTO { if (value === undefined || value === null) { return undefined; } - let execution: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined; + let execution: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecutionDTO | undefined; if (value.execution instanceof types.ProcessExecution) { execution = ProcessExecutionDTO.from(value.execution); } else if (value.execution instanceof types.ShellExecution) { execution = ShellExecutionDTO.from(value.execution); - } else if ((value).execution2 && (value).execution2 instanceof types.CustomExecution2) { - execution = CustomExecution2DTO.from((value).execution2); + } else if ((value).execution2 && (value).execution2 instanceof types.CustomExecution) { + execution = CustomExecutionDTO.from((value).execution2); } const definition: tasks.TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition); @@ -373,9 +373,9 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { protected _handleCounter: number; protected _handlers: Map; protected _taskExecutions: Map; - protected _providedCustomExecutions2: Map; + protected _providedCustomExecutions2: Map; private _notProvidedCustomExecutions: Set; // Used for custom executions tasks that are created and run through executeTask. - protected _activeCustomExecutions2: Map; + protected _activeCustomExecutions2: Map; private _lastStartedTask: string | undefined; protected readonly _onDidExecuteTask: Emitter = new Emitter(); protected readonly _onDidTerminateTask: Emitter = new Emitter(); @@ -399,9 +399,9 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { this._handleCounter = 0; this._handlers = new Map(); this._taskExecutions = new Map(); - this._providedCustomExecutions2 = new Map(); + this._providedCustomExecutions2 = new Map(); this._notProvidedCustomExecutions = new Set(); - this._activeCustomExecutions2 = new Map(); + this._activeCustomExecutions2 = new Map(); } public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable { @@ -454,15 +454,15 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { } public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): Promise { - const execution2: types.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id); - if (execution2) { + const customExecution: types.CustomExecution | undefined = this._providedCustomExecutions2.get(execution.id); + if (customExecution) { if (this._activeCustomExecutions2.get(execution.id) !== undefined) { throw new Error('We should not be trying to start the same custom task executions twice.'); } // Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task. - this._activeCustomExecutions2.set(execution.id, execution2); - this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback()); + this._activeCustomExecutions2.set(execution.id, customExecution); + this._terminalService.attachPtyToTerminal(terminalId, await customExecution.callback()); } this._lastStartedTask = execution.id; @@ -573,8 +573,8 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.'); } - if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) { - await this.addCustomExecution2(resolvedTaskDTO, resolvedTask, true); + if (CustomExecutionDTO.is(resolvedTaskDTO.execution)) { + await this.addCustomExecution(resolvedTaskDTO, resolvedTask, true); } return await this.resolveTaskInternal(resolvedTaskDTO); @@ -588,12 +588,12 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { return this._handleCounter++; } - protected async addCustomExecution2(taskDTO: tasks.TaskDTO, task: vscode.Task2, isProvided: boolean): Promise { + protected async addCustomExecution(taskDTO: tasks.TaskDTO, task: vscode.Task2, isProvided: boolean): Promise { const taskId = await this._proxy.$createTaskId(taskDTO); if (!isProvided && !this._providedCustomExecutions2.has(taskId)) { this._notProvidedCustomExecutions.add(taskId); } - this._providedCustomExecutions2.set(taskId, (task).execution2); + this._providedCustomExecutions2.set(taskId, (task).execution2); } protected async getTaskExecution(execution: tasks.TaskExecutionDTO | string, task?: vscode.Task): Promise { @@ -619,7 +619,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { } private customExecutionComplete(execution: tasks.TaskExecutionDTO): void { - const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id); + const extensionCallback2: vscode.CustomExecution | undefined = this._activeCustomExecutions2.get(execution.id); if (extensionCallback2) { this._activeCustomExecutions2.delete(execution.id); } @@ -674,8 +674,8 @@ export class WorkerExtHostTask extends ExtHostTaskBase { // If this task is a custom execution, then we need to save it away // in the provided custom execution map that is cleaned up after the // task is executed. - if (CustomExecution2DTO.is(dto.execution)) { - await this.addCustomExecution2(dto, task, false); + if (CustomExecutionDTO.is(dto.execution)) { + await this.addCustomExecution(dto, task, false); } else { throw new Error('Not implemented'); } @@ -692,12 +692,12 @@ export class WorkerExtHostTask extends ExtHostTaskBase { } const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension); - if (taskDTO && CustomExecution2DTO.is(taskDTO.execution)) { + if (taskDTO && CustomExecutionDTO.is(taskDTO.execution)) { taskDTOs.push(taskDTO); // The ID is calculated on the main thread task side, so, let's call into it here. // We need the task id's pre-computed for custom task executions because when OnDidStartTask // is invoked, we have to be able to map it back to our data. - taskIdPromises.push(this.addCustomExecution2(taskDTO, task, true)); + taskIdPromises.push(this.addCustomExecution(taskDTO, task, true)); } else { console.warn('Only custom execution tasks supported.'); } @@ -710,7 +710,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase { } protected async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise { - if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) { + if (CustomExecutionDTO.is(resolvedTaskDTO.execution)) { return resolvedTaskDTO; } else { console.warn('Only custom execution tasks supported.'); diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 368f057d8d8..c109ec77bf6 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -299,7 +299,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal; public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal; public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; - public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise; + public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise; public abstract $requestAvailableShells(): Promise; public abstract $requestDefaultShellAndArgs(useAutomationShell: boolean): Promise; public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; @@ -577,7 +577,7 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService { throw new Error('Not implemented'); } - public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { + public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { throw new Error('Not implemented'); } diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 7c462183bc7..24b55f765df 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -166,8 +166,8 @@ interface TreeNode extends IDisposable { class ExtHostTreeView extends Disposable { - private static LABEL_HANDLE_PREFIX = '0'; - private static ID_HANDLE_PREFIX = '1'; + private static readonly LABEL_HANDLE_PREFIX = '0'; + private static readonly ID_HANDLE_PREFIX = '1'; private readonly dataProvider: vscode.TreeDataProvider; @@ -540,7 +540,7 @@ class ExtHostTreeView extends Disposable { private getLightIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined { if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon)) { if (typeof extensionTreeItem.iconPath === 'string' - || extensionTreeItem.iconPath instanceof URI) { + || URI.isUri(extensionTreeItem.iconPath)) { return this.getIconPath(extensionTreeItem.iconPath); } return this.getIconPath((<{ light: string | URI; dark: string | URI }>extensionTreeItem.iconPath).light); @@ -556,7 +556,7 @@ class ExtHostTreeView extends Disposable { } private getIconPath(iconPath: string | URI): URI { - if (iconPath instanceof URI) { + if (URI.isUri(iconPath)) { return iconPath; } return URI.file(iconPath); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index cecf59ef4f1..608b6d90707 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -291,7 +291,7 @@ export namespace MarkdownString { return part; } data = cloneAndChange(data, value => { - if (value instanceof URI) { + if (URI.isUri(value)) { const key = `__uri_${Math.random().toString(16).slice(2, 8)}`; bucket[key] = value; return key; @@ -303,9 +303,7 @@ export namespace MarkdownString { } export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString { - const ret = new htmlContent.MarkdownString(value.value); - ret.isTrusted = value.isTrusted; - return ret; + return new htmlContent.MarkdownString(value.value, value.isTrusted); } export function fromStrict(value: string | types.MarkdownString): undefined | string | htmlContent.IMarkdownString { @@ -859,7 +857,7 @@ export namespace SignatureInformation { return { label: info.label, documentation: info.documentation ? MarkdownString.fromStrict(info.documentation) : undefined, - parameters: info.parameters && info.parameters.map(ParameterInformation.from) + parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.from) : [] }; } @@ -867,7 +865,7 @@ export namespace SignatureInformation { return { label: info.label, documentation: htmlContent.isMarkdownString(info.documentation) ? MarkdownString.to(info.documentation) : info.documentation, - parameters: info.parameters && info.parameters.map(ParameterInformation.to) + parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.to) : [] }; } } @@ -878,7 +876,7 @@ export namespace SignatureHelp { return { activeSignature: help.activeSignature, activeParameter: help.activeParameter, - signatures: help.signatures && help.signatures.map(SignatureInformation.from) + signatures: Array.isArray(help.signatures) ? help.signatures.map(SignatureInformation.from) : [], }; } @@ -886,7 +884,7 @@ export namespace SignatureHelp { return { activeSignature: help.activeSignature, activeParameter: help.activeParameter, - signatures: help.signatures && help.signatures.map(SignatureInformation.to) + signatures: Array.isArray(help.signatures) ? help.signatures.map(SignatureInformation.to) : [], }; } } @@ -1163,13 +1161,3 @@ export namespace LogLevel { return types.LogLevel.Info; } } -export namespace WebviewContentState { - export function from(state: vscode.WebviewContentState): modes.WebviewContentState { - switch (state) { - case types.WebviewContentState.Readonly: return modes.WebviewContentState.Readonly; - case types.WebviewContentState.Unchanged: return modes.WebviewContentState.Unchanged; - case types.WebviewContentState.Dirty: return modes.WebviewContentState.Dirty; - default: throw new Error('Unknown vscode.WebviewContentState'); - } - } -} diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 79cd4779316..a5644289cf7 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -614,12 +614,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } has(uri: URI): boolean { - for (const edit of this._edits) { - if (edit._type === 2 && edit.uri.toString() === uri.toString()) { - return true; - } - } - return false; + return this._edits.some(edit => edit._type === 2 && edit.uri.toString() === uri.toString()); } set(uri: URI, edits: TextEdit[]): void { @@ -1166,22 +1161,22 @@ export class CallHierarchyItem { export class CallHierarchyIncomingCall { - source: vscode.CallHierarchyItem; - sourceRanges: vscode.Range[]; + from: vscode.CallHierarchyItem; + fromRanges: vscode.Range[]; - constructor(item: vscode.CallHierarchyItem, sourceRanges: vscode.Range[]) { - this.sourceRanges = sourceRanges; - this.source = item; + constructor(item: vscode.CallHierarchyItem, fromRanges: vscode.Range[]) { + this.fromRanges = fromRanges; + this.from = item; } } export class CallHierarchyOutgoingCall { - target: vscode.CallHierarchyItem; - sourceRanges: vscode.Range[]; + to: vscode.CallHierarchyItem; + fromRanges: vscode.Range[]; - constructor(item: vscode.CallHierarchyItem, sourceRanges: vscode.Range[]) { - this.sourceRanges = sourceRanges; - this.target = item; + constructor(item: vscode.CallHierarchyItem, fromRanges: vscode.Range[]) { + this.fromRanges = fromRanges; + this.to = item; } } @@ -1227,7 +1222,9 @@ export class MarkdownString { appendText(value: string): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this.value += value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); + this.value += value + .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') + .replace('\n', '\n\n'); return this; } @@ -1472,7 +1469,7 @@ export class DocumentLink { tooltip?: string; constructor(range: Range, target: URI | undefined) { - if (target && !(target instanceof URI)) { + if (target && !(URI.isUri(target))) { throw illegalArgument('target'); } if (!Range.isRange(range) || range.isEmpty) { @@ -1624,6 +1621,7 @@ export class ProcessExecution implements vscode.ProcessExecution { if (typeof process !== 'string') { throw illegalArgument('process'); } + this._args = []; this._process = process; if (varg1 !== undefined) { if (Array.isArray(varg1)) { @@ -1633,9 +1631,6 @@ export class ProcessExecution implements vscode.ProcessExecution { this._options = varg1; } } - if (this._args === undefined) { - this._args = []; - } } @@ -1687,9 +1682,9 @@ export class ProcessExecution implements vscode.ProcessExecution { @es5ClassCompat export class ShellExecution implements vscode.ShellExecution { - private _commandLine: string; - private _command: string | vscode.ShellQuotedString; - private _args: (string | vscode.ShellQuotedString)[]; + private _commandLine: string | undefined; + private _command: string | vscode.ShellQuotedString | undefined; + private _args: (string | vscode.ShellQuotedString)[] = []; private _options: vscode.ShellExecutionOptions | undefined; constructor(commandLine: string, options?: vscode.ShellExecutionOptions); @@ -1714,11 +1709,11 @@ export class ShellExecution implements vscode.ShellExecution { } } - get commandLine(): string { + get commandLine(): string | undefined { return this._commandLine; } - set commandLine(value: string) { + set commandLine(value: string | undefined) { if (typeof value !== 'string') { throw illegalArgument('commandLine'); } @@ -1726,7 +1721,7 @@ export class ShellExecution implements vscode.ShellExecution { } get command(): string | vscode.ShellQuotedString { - return this._command; + return this._command ? this._command : ''; } set command(value: string | vscode.ShellQuotedString) { @@ -1781,7 +1776,7 @@ export enum TaskScope { Workspace = 2 } -export class CustomExecution2 implements vscode.CustomExecution2 { +export class CustomExecution implements vscode.CustomExecution { private _callback: () => Thenable; constructor(callback: () => Thenable) { this._callback = callback; @@ -1812,7 +1807,7 @@ export class Task implements vscode.Task2 { private _definition: vscode.TaskDefinition; private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined; private _name: string; - private _execution: ProcessExecution | ShellExecution | CustomExecution2 | undefined; + private _execution: ProcessExecution | ShellExecution | CustomExecution | undefined; private _problemMatchers: string[]; private _hasDefinedMatchers: boolean; private _isBackground: boolean; @@ -1821,26 +1816,26 @@ export class Task implements vscode.Task2 { private _presentationOptions: vscode.TaskPresentationOptions; private _runOptions: vscode.RunOptions; - constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]); - constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]); + constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); + constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) { - this.definition = definition; + this._definition = this.definition = definition; let problemMatchers: string | string[]; if (typeof arg2 === 'string') { - this.name = arg2; - this.source = arg3; + this._name = this.name = arg2; + this._source = this.source = arg3; this.execution = arg4; problemMatchers = arg5; } else if (arg2 === TaskScope.Global || arg2 === TaskScope.Workspace) { this.target = arg2; - this.name = arg3; - this.source = arg4; + this._name = this.name = arg3; + this._source = this.source = arg4; this.execution = arg5; problemMatchers = arg6; } else { this.target = arg2; - this.name = arg3; - this.source = arg4; + this._name = this.name = arg3; + this._source = this.source = arg4; this.execution = arg5; problemMatchers = arg6; } @@ -1887,7 +1882,7 @@ export class Task implements vscode.Task2 { type: Task.ShellType, id: this._execution.computeId() }; - } else if (this._execution instanceof CustomExecution2) { + } else if (this._execution instanceof CustomExecution) { this._definition = { type: Task.ExtensionCallbackType, id: this._execution.computeId() @@ -1934,18 +1929,18 @@ export class Task implements vscode.Task2 { } get execution(): ProcessExecution | ShellExecution | undefined { - return (this._execution instanceof CustomExecution2) ? undefined : this._execution; + return (this._execution instanceof CustomExecution) ? undefined : this._execution; } set execution(value: ProcessExecution | ShellExecution | undefined) { this.execution2 = value; } - get execution2(): ProcessExecution | ShellExecution | CustomExecution2 | undefined { + get execution2(): ProcessExecution | ShellExecution | CustomExecution | undefined { return this._execution; } - set execution2(value: ProcessExecution | ShellExecution | CustomExecution2 | undefined) { + set execution2(value: ProcessExecution | ShellExecution | CustomExecution | undefined) { if (value === null) { value = undefined; } @@ -2059,7 +2054,7 @@ export class TreeItem { constructor(label: string | vscode.TreeItemLabel, collapsibleState?: vscode.TreeItemCollapsibleState) constructor(resourceUri: URI, collapsibleState?: vscode.TreeItemCollapsibleState) constructor(arg1: string | vscode.TreeItemLabel | URI, public collapsibleState: vscode.TreeItemCollapsibleState = TreeItemCollapsibleState.None) { - if (arg1 instanceof URI) { + if (URI.isUri(arg1)) { this.resourceUri = arg1; } else { this.label = arg1; diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 9abd2df4d11..6526b1361d4 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -7,19 +7,20 @@ import { Emitter, Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as modes from 'vs/editor/common/modes'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import * as vscode from 'vscode'; import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol'; -import { Disposable, WebviewContentState } from './extHostTypes'; +import { Disposable } from './extHostTypes'; type IconPath = URI | { light: URI, dark: URI }; export class ExtHostWebview implements vscode.Webview { - private _html: string; + private _html: string = ''; private _isDisposed: boolean = false; + private _hasCalledAsWebviewUri = false; public readonly _onMessageEmitter = new Emitter(); public readonly onDidReceiveMessage: Event = this._onMessageEmitter.event; @@ -28,7 +29,8 @@ export class ExtHostWebview implements vscode.Webview { private readonly _handle: WebviewPanelHandle, private readonly _proxy: MainThreadWebviewsShape, private _options: vscode.WebviewOptions, - private readonly _initData: WebviewInitData + private readonly _initData: WebviewInitData, + private readonly _extensionId: ExtensionIdentifier | undefined, ) { } public dispose() { @@ -36,6 +38,7 @@ export class ExtHostWebview implements vscode.Webview { } public asWebviewUri(resource: vscode.Uri): vscode.Uri { + this._hasCalledAsWebviewUri = true; return asWebviewUri(this._initData, this._handle, resource); } @@ -53,6 +56,12 @@ export class ExtHostWebview implements vscode.Webview { this.assertNotDisposed(); if (this._html !== value) { this._html = value; + if (this._initData.isExtensionDevelopmentDebug && this._extensionId && !this._hasCalledAsWebviewUri) { + if (/(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) { + this._hasCalledAsWebviewUri = true; + console.warn(`${this._extensionId.value} created a webview that appears to use the vscode-resource scheme directly. Please migrate to use the 'webview.asWebviewUri' api instead: https://aka.ms/vscode-webview-use-aswebviewuri`); + } + } this._proxy.$setHtml(this._handle, value); } } @@ -93,9 +102,6 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor { private _viewColumn: vscode.ViewColumn | undefined; private _visible: boolean = true; private _active: boolean = true; - private _state: vscode.WebviewEditorState = { - contentState: WebviewContentState.Readonly, - }; _isDisposed: boolean = false; @@ -215,15 +221,6 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor { this._visible = value; } - public get editorState(): vscode.WebviewEditorState { - return this._state; - } - - public set editorState(newState: vscode.WebviewEditorState) { - this._state = newState; - this._proxy.$setState(this._handle, typeConverters.WebviewContentState.from(newState.contentState)); - } - private readonly _onWillSave = new Emitter<{ waitUntil: (thenable: Thenable) => void }>(); public readonly onWillSave = this._onWillSave.event; @@ -290,7 +287,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { const handle = ExtHostWebviews.newHandle(); this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, convertWebviewOptions(options), extension.identifier, extension.extensionLocation); - const webview = new ExtHostWebview(handle, this._proxy, options, this.initData); + const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, extension.identifier); const panel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, viewColumn, options, webview); this._webviewPanels.set(handle, panel); return panel; @@ -405,7 +402,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { return Promise.reject(new Error(`No serializer found for '${viewType}'`)); } - const webview = new ExtHostWebview(webviewHandle, this._proxy, options, this.initData); + const webview = new ExtHostWebview(webviewHandle, this._proxy, options, this.initData, undefined); const revivedPanel = new ExtHostWebviewEditor(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(webviewHandle, revivedPanel); return Promise.resolve(serializer.deserializeWebviewPanel(revivedPanel, state)); @@ -432,7 +429,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { this._proxy.$setExtension(handle, extension.identifier, extension.extensionLocation); - const webview = new ExtHostWebview(handle, this._proxy, options, this.initData); + const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, extension.identifier); const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(handle, revivedPanel); return Promise.resolve(provider.resolveWebviewEditor(URI.revive(resource), revivedPanel)); diff --git a/src/vs/workbench/api/common/extHostWindow.ts b/src/vs/workbench/api/common/extHostWindow.ts index 7017c29788e..8ce82ac8b2d 100644 --- a/src/vs/workbench/api/common/extHostWindow.ts +++ b/src/vs/workbench/api/common/extHostWindow.ts @@ -54,14 +54,14 @@ export class ExtHostWindow implements ExtHostWindowShape { return this._proxy.$openUri(stringOrUri, options); } - async resolveExternalUri(uri: URI, options: IOpenUriOptions): Promise { + async asExternalUri(uri: URI, options: IOpenUriOptions): Promise { if (isFalsyOrWhitespace(uri.scheme)) { return Promise.reject('Invalid scheme - cannot be empty'); } else if (!new Set([Schemas.http, Schemas.https]).has(uri.scheme)) { return Promise.reject(`Invalid scheme '${uri.scheme}'`); } - const result = await this._proxy.$resolveExternalUri(uri, options); + const result = await this._proxy.$asExternalUri(uri, options); return URI.from(result); } } diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 0dd6facb692..3133acfba2c 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -12,7 +12,7 @@ import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } fr import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { MenuId, MenuRegistry, ILocalizedString, IMenuItem } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; -import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; namespace schema { @@ -387,7 +387,7 @@ commandsExtensionPoint.setHandler(extensions => { } }); -let _menuRegistrations: IDisposable[] = []; +const _menuRegistrations = new DisposableStore(); ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>({ extensionPoint: 'menus', @@ -395,7 +395,7 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM }).setHandler(extensions => { // remove all previous menu registrations - _menuRegistrations = dispose(_menuRegistrations); + _menuRegistrations.clear(); for (let extension of extensions) { const { value, collector } = extension; @@ -450,7 +450,7 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM order, when: ContextKeyExpr.deserialize(item.when) } as IMenuItem); - _menuRegistrations.push(registration); + _menuRegistrations.add(registration); } }); } diff --git a/src/vs/workbench/api/common/shared/tasks.ts b/src/vs/workbench/api/common/shared/tasks.ts index b8fab0b5a02..e31f4e6b807 100644 --- a/src/vs/workbench/api/common/shared/tasks.ts +++ b/src/vs/workbench/api/common/shared/tasks.ts @@ -66,8 +66,8 @@ export interface ShellExecutionDTO { options?: ShellExecutionOptionsDTO; } -export interface CustomExecution2DTO { - customExecution: 'customExecution2'; +export interface CustomExecutionDTO { + customExecution: 'customExecution'; } export interface TaskSourceDTO { @@ -84,7 +84,7 @@ export interface TaskHandleDTO { export interface TaskDTO { _id: string; name?: string; - execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecution2DTO | undefined; + execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | undefined; definition: TaskDefinitionDTO; isBackground?: boolean; source: TaskSourceDTO; diff --git a/src/vs/workbench/api/common/shared/webview.ts b/src/vs/workbench/api/common/shared/webview.ts index 6b44c25c76b..6d5a52928f8 100644 --- a/src/vs/workbench/api/common/shared/webview.ts +++ b/src/vs/workbench/api/common/shared/webview.ts @@ -7,6 +7,7 @@ import { URI } from 'vs/base/common/uri'; import * as vscode from 'vscode'; export interface WebviewInitData { + readonly isExtensionDevelopmentDebug: boolean; readonly webviewResourceRoot: string; readonly webviewCspSource: string; } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 71d8b818b72..5c167085086 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -19,7 +19,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; -import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecution2DTO, HandlerData } from 'vs/workbench/api/common/extHostTask'; +import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerData } from 'vs/workbench/api/common/extHostTask'; import { Schemas } from 'vs/base/common/network'; export class ExtHostTask extends ExtHostTaskBase { @@ -56,8 +56,8 @@ export class ExtHostTask extends ExtHostTaskBase { // If this task is a custom execution, then we need to save it away // in the provided custom execution map that is cleaned up after the // task is executed. - if (CustomExecution2DTO.is(dto.execution)) { - await this.addCustomExecution2(dto, task, false); + if (CustomExecutionDTO.is(dto.execution)) { + await this.addCustomExecution(dto, task, false); } return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task)); @@ -76,11 +76,11 @@ export class ExtHostTask extends ExtHostTaskBase { if (taskDTO) { taskDTOs.push(taskDTO); - if (CustomExecution2DTO.is(taskDTO.execution)) { + if (CustomExecutionDTO.is(taskDTO.execution)) { // The ID is calculated on the main thread task side, so, let's call into it here. // We need the task id's pre-computed for custom task executions because when OnDidStartTask // is invoked, we have to be able to map it back to our data. - taskIdPromises.push(this.addCustomExecution2(taskDTO, task, true)); + taskIdPromises.push(this.addCustomExecution(taskDTO, task, true)); } } } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 13831193a59..1928820a110 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -123,7 +123,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider); } - public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { + public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { const shellLaunchConfig: IShellLaunchConfig = { name: shellLaunchConfigDto.name, executable: shellLaunchConfigDto.executable, @@ -155,17 +155,23 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { } } - const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents); - // Get the environment - const apiLastActiveWorkspace = await this._extHostWorkspace.getWorkspaceFolder(activeWorkspaceRootUri); - const lastActiveWorkspace = apiLastActiveWorkspace ? { - uri: apiLastActiveWorkspace.uri, - name: apiLastActiveWorkspace.name, - index: apiLastActiveWorkspace.index, - toResource: () => { - throw new Error('Not implemented'); + let lastActiveWorkspace: IWorkspaceFolder | null = null; + let activeWorkspaceRootUri: URI | undefined; + if (activeWorkspaceRootUriComponents) { + let activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents); + // Get the environment + const apiLastActiveWorkspace = await this._extHostWorkspace.getWorkspaceFolder(activeWorkspaceRootUri); + if (apiLastActiveWorkspace) { + lastActiveWorkspace = { + uri: apiLastActiveWorkspace.uri, + name: apiLastActiveWorkspace.name, + index: apiLastActiveWorkspace.index, + toResource: () => { + throw new Error('Not implemented'); + } + }; } - } as IWorkspaceFolder : null; + } // Get the initial cwd const terminalConfig = configProvider.getConfiguration('terminal.integrated'); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index afd82468c06..4781f226762 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -32,7 +32,7 @@ class WorkerRequireInterceptor extends RequireInterceptor { export class ExtHostExtensionService extends AbstractExtHostExtensionService { - private _fakeModules: WorkerRequireInterceptor; + private _fakeModules?: WorkerRequireInterceptor; protected async _beforeAlmostReadyToRunExtensions(): Promise { // initialize API and register actors @@ -57,7 +57,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { const _exports = {}; const _module = { exports: _exports }; const _require = (request: string) => { - const result = this._fakeModules.getModule(request, module); + const result = this._fakeModules!.getModule(request, module); if (result === undefined) { throw new Error(`Cannot load module '${request}'`); } diff --git a/src/vs/workbench/browser/actions.ts b/src/vs/workbench/browser/actions.ts index aac3da30445..2cf27b60ae6 100644 --- a/src/vs/workbench/browser/actions.ts +++ b/src/vs/workbench/browser/actions.ts @@ -57,13 +57,7 @@ export class ContributableActionProvider implements IActionProvider { const context = this.toContext(tree, element); const contributors = this.registry.getActionBarContributors(Scope.VIEWER); - for (const contributor of contributors) { - if (contributor.hasActions(context)) { - return true; - } - } - - return false; + return contributors.some(contributor => contributor.hasActions(context)); } getActions(tree: ITree, element: unknown): ReadonlyArray { @@ -156,7 +150,7 @@ export interface IActionBarRegistry { class ActionBarRegistry implements IActionBarRegistry { private readonly actionBarContributorConstructors: { scope: string; ctor: IConstructorSignature0; }[] = []; private readonly actionBarContributorInstances: Map = new Map(); - private instantiationService!: IInstantiationService; + private instantiationService: IInstantiationService | undefined; start(accessor: ServicesAccessor): void { this.instantiationService = accessor.get(IInstantiationService); @@ -168,13 +162,15 @@ class ActionBarRegistry implements IActionBarRegistry { } private createActionBarContributor(scope: string, ctor: IConstructorSignature0): void { - const instance = this.instantiationService.createInstance(ctor); - let target = this.actionBarContributorInstances.get(scope); - if (!target) { - target = []; - this.actionBarContributorInstances.set(scope, target); + if (this.instantiationService) { + const instance = this.instantiationService.createInstance(ctor); + let target = this.actionBarContributorInstances.get(scope); + if (!target) { + target = []; + this.actionBarContributorInstances.set(scope, target); + } + target.push(instance); } - target.push(instance); } private getContributors(scope: string): ActionBarContributor[] { diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 1447d3f1b77..051b58f56ad 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -29,7 +29,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'v class InspectContextKeysAction extends Action { static readonly ID = 'workbench.action.inspectContextKeys'; - static LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); + static readonly LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); constructor( id: string, @@ -91,7 +91,7 @@ class InspectContextKeysAction extends Action { class ToggleScreencastModeAction extends Action { static readonly ID = 'workbench.action.toggleScreencastMode'; - static LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode"); + static readonly LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode"); static disposable: IDisposable | undefined; @@ -195,7 +195,7 @@ class ToggleScreencastModeAction extends Action { class LogStorageAction extends Action { static readonly ID = 'workbench.action.logStorage'; - static LABEL = nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"); + static readonly LABEL = nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"); constructor( id: string, diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 190f5dcd853..a10a3d58b9c 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -340,7 +340,7 @@ class ToggleTabsVisibilityAction extends Action { } registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { - primary: undefined!, + primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, }, linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, } }), 'View: Toggle Tab Visibility', viewCategory); @@ -396,7 +396,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ export class ToggleMenuBarAction extends Action { static readonly ID = 'workbench.action.toggleMenuBar'; - static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar"); + static readonly LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar"); private static readonly menuBarVisibilityKey = 'window.menuBarVisibility'; @@ -446,7 +446,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { export abstract class BaseResizeViewAction extends Action { - protected static RESIZE_INCREMENT = 6.5; // This is a media-size percentage + protected static readonly RESIZE_INCREMENT = 6.5; // This is a media-size percentage constructor( id: string, diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index d33cb450d5d..81fb3c49efe 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -17,6 +17,11 @@ import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { DataTree } from 'vs/base/browser/ui/tree/dataTree'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; + +function isLegacyTree(widget: ListWidget): widget is ITree { + return widget instanceof Tree; +} function ensureDOMFocus(widget: ListWidget | undefined): void { // it can happen that one of the commands is executed while @@ -833,3 +838,67 @@ CommandsRegistry.registerCommand({ } } }); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.scrollUp', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyMod.CtrlCmd | KeyCode.UpArrow, + handler: accessor => { + const focused = accessor.get(IListService).lastFocusedList; + + if (!focused || isLegacyTree(focused)) { + return; + } + + focused.scrollTop -= 10; + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.scrollDown', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, + handler: accessor => { + const focused = accessor.get(IListService).lastFocusedList; + + if (!focused || isLegacyTree(focused)) { + return; + } + + focused.scrollTop += 10; + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.scrollLeft', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, + handler: accessor => { + const focused = accessor.get(IListService).lastFocusedList; + + if (!focused || isLegacyTree(focused)) { + return; + } + + focused.scrollLeft -= 10; + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.scrollRight', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyMod.CtrlCmd | KeyCode.RightArrow, + handler: accessor => { + const focused = accessor.get(IListService).lastFocusedList; + + if (!focused || isLegacyTree(focused)) { + return; + } + + focused.scrollLeft += 10; + } +}); diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index 621f41f00f6..fae9742c7bf 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -68,9 +68,19 @@ abstract class BaseNavigationAction extends Action { return false; } - const activePanelId = this.panelService.getActivePanel()!.getId(); + const activePanel = this.panelService.getActivePanel(); + if (!activePanel) { + return false; + } - return this.panelService.openPanel(activePanelId, true)!; + const activePanelId = activePanel.getId(); + + const res = this.panelService.openPanel(activePanelId, true); + if (!res) { + return false; + } + + return res; } protected async navigateToSidebar(): Promise { @@ -84,8 +94,8 @@ abstract class BaseNavigationAction extends Action { } const activeViewletId = activeViewlet.getId(); - const value = await this.viewletService.openViewlet(activeViewletId, true); - return value === null ? false : value; + const viewlet = await this.viewletService.openViewlet(activeViewletId, true); + return !!viewlet; } protected navigateAcrossEditorGroup(direction: GroupDirection): boolean { diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index 9a0bb2545e2..55a96615e36 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -37,7 +37,7 @@ export const inRecentFilesPickerContextKey = 'inRecentFilesPicker'; abstract class BaseOpenRecentAction extends Action { private removeFromRecentlyOpened: IQuickInputButton = { - iconClass: 'action-remove-from-recently-opened', + iconClass: 'codicon-close', tooltip: nls.localize('remove', "Remove from Recently Opened") }; @@ -193,7 +193,7 @@ class QuickOpenRecentAction extends BaseOpenRecentAction { class ToggleFullScreenAction extends Action { static readonly ID = 'workbench.action.toggleFullScreen'; - static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); + static readonly LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); constructor( id: string, @@ -211,7 +211,7 @@ class ToggleFullScreenAction extends Action { export class ReloadWindowAction extends Action { static readonly ID = 'workbench.action.reloadWindow'; - static LABEL = nls.localize('reloadWindow', "Reload Window"); + static readonly LABEL = nls.localize('reloadWindow', "Reload Window"); constructor( id: string, @@ -249,7 +249,7 @@ class ShowAboutDialogAction extends Action { export class NewWindowAction extends Action { static readonly ID = 'workbench.action.newWindow'; - static LABEL = nls.localize('newWindow', "New Window"); + static readonly LABEL = nls.localize('newWindow', "New Window"); constructor( id: string, diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 0b46377515b..b7d74392423 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -21,11 +21,12 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IHostService } from 'vs/workbench/services/host/browser/host'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class OpenFileAction extends Action { static readonly ID = 'workbench.action.files.openFile'; - static LABEL = nls.localize('openFile', "Open File..."); + static readonly LABEL = nls.localize('openFile', "Open File..."); constructor( id: string, @@ -43,7 +44,7 @@ export class OpenFileAction extends Action { export class OpenFolderAction extends Action { static readonly ID = 'workbench.action.files.openFolder'; - static LABEL = nls.localize('openFolder', "Open Folder..."); + static readonly LABEL = nls.localize('openFolder', "Open Folder..."); constructor( id: string, @@ -61,7 +62,7 @@ export class OpenFolderAction extends Action { export class OpenFileFolderAction extends Action { static readonly ID = 'workbench.action.files.openFileFolder'; - static LABEL = nls.localize('openFileFolder', "Open..."); + static readonly LABEL = nls.localize('openFileFolder', "Open..."); constructor( id: string, @@ -79,7 +80,7 @@ export class OpenFileFolderAction extends Action { export class OpenWorkspaceAction extends Action { static readonly ID = 'workbench.action.openWorkspace'; - static LABEL = nls.localize('openWorkspaceAction', "Open Workspace..."); + static readonly LABEL = nls.localize('openWorkspaceAction', "Open Workspace..."); constructor( id: string, @@ -97,14 +98,15 @@ export class OpenWorkspaceAction extends Action { export class CloseWorkspaceAction extends Action { static readonly ID = 'workbench.action.closeFolder'; - static LABEL = nls.localize('closeWorkspace', "Close Workspace"); + static readonly LABEL = nls.localize('closeWorkspace', "Close Workspace"); constructor( id: string, label: string, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @INotificationService private readonly notificationService: INotificationService, - @IHostService private readonly hostService: IHostService + @IHostService private readonly hostService: IHostService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { super(id, label); } @@ -116,7 +118,7 @@ export class CloseWorkspaceAction extends Action { return Promise.resolve(undefined); } - return this.hostService.closeWorkspace(); + return this.hostService.openWindow({ forceReuseWindow: true, remoteAuthority: this.environmentService.configuration.remoteAuthority }); } } @@ -148,7 +150,7 @@ export class OpenWorkspaceConfigFileAction extends Action { export class AddRootFolderAction extends Action { static readonly ID = 'workbench.action.addRootFolder'; - static LABEL = ADD_ROOT_FOLDER_LABEL; + static readonly LABEL = ADD_ROOT_FOLDER_LABEL; constructor( id: string, @@ -166,7 +168,7 @@ export class AddRootFolderAction extends Action { export class GlobalRemoveRootFolderAction extends Action { static readonly ID = 'workbench.action.removeRootFolder'; - static LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace..."); + static readonly LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace..."); constructor( id: string, diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 1c8f30a8f20..6414496a6d3 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -14,6 +14,8 @@ import { IConstructorSignature0, IInstantiationService } from 'vs/platform/insta import { trackFocus, Dimension } from 'vs/base/browser/dom'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Disposable } from 'vs/base/common/lifecycle'; +import { assertIsDefined } from 'vs/base/common/types'; +import { find } from 'vs/base/common/arrays'; /** * Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite @@ -35,10 +37,10 @@ export abstract class Composite extends Component implements IComposite { private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - private _onDidFocus!: Emitter; + private _onDidFocus: Emitter | undefined; get onDidFocus(): Event { if (!this._onDidFocus) { - this.registerFocusTrackEvents(); + this._onDidFocus = this.registerFocusTrackEvents().onDidFocus; } return this._onDidFocus.event; @@ -50,28 +52,32 @@ export abstract class Composite extends Component implements IComposite { } } - private _onDidBlur!: Emitter; + private _onDidBlur: Emitter | undefined; get onDidBlur(): Event { if (!this._onDidBlur) { - this.registerFocusTrackEvents(); + this._onDidBlur = this.registerFocusTrackEvents().onDidBlur; } return this._onDidBlur.event; } - private registerFocusTrackEvents(): void { - this._onDidFocus = this._register(new Emitter()); - this._onDidBlur = this._register(new Emitter()); + private registerFocusTrackEvents(): { onDidFocus: Emitter, onDidBlur: Emitter } { + const container = assertIsDefined(this.getContainer()); + const focusTracker = this._register(trackFocus(container)); - const focusTracker = this._register(trackFocus(this.getContainer())); - this._register(focusTracker.onDidFocus(() => this._onDidFocus.fire())); - this._register(focusTracker.onDidBlur(() => this._onDidBlur.fire())); + const onDidFocus = this._onDidFocus = this._register(new Emitter()); + this._register(focusTracker.onDidFocus(() => onDidFocus.fire())); + + const onDidBlur = this._onDidBlur = this._register(new Emitter()); + this._register(focusTracker.onDidBlur(() => onDidBlur.fire())); + + return { onDidFocus, onDidBlur }; } protected actionRunner: IActionRunner | undefined; private visible: boolean; - private parent!: HTMLElement; + private parent: HTMLElement | undefined; constructor( id: string, @@ -112,7 +118,7 @@ export abstract class Composite extends Component implements IComposite { /** * Returns the container this composite is being build in. */ - getContainer(): HTMLElement { + getContainer(): HTMLElement | undefined { return this.parent; } @@ -253,7 +259,7 @@ export abstract class CompositeRegistry extends Disposable private composites: CompositeDescriptor[] = []; protected registerComposite(descriptor: CompositeDescriptor): void { - if (this.compositeById(descriptor.id) !== null) { + if (this.compositeById(descriptor.id)) { return; } @@ -271,7 +277,7 @@ export abstract class CompositeRegistry extends Disposable this._onDidDeregister.fire(descriptor); } - getComposite(id: string): CompositeDescriptor | null { + getComposite(id: string): CompositeDescriptor | undefined { return this.compositeById(id); } @@ -279,13 +285,7 @@ export abstract class CompositeRegistry extends Disposable return this.composites.slice(0); } - private compositeById(id: string): CompositeDescriptor | null { - for (const composite of this.composites) { - if (composite.id === id) { - return composite; - } - } - - return null; + private compositeById(id: string): CompositeDescriptor | undefined { + return find(this.composites, composite => composite.id === id); } } diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 34640af8e1e..2a88e72a81c 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -88,41 +88,6 @@ export class WorkbenchContextKeysHandler extends Disposable { ) { super(); - this.initContextKeys(); - this.registerListeners(); - } - - private registerListeners(): void { - this.editorGroupService.whenRestored.then(() => this.updateEditorContextKeys()); - - this._register(this.editorService.onDidActiveEditorChange(() => this.updateEditorContextKeys())); - this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateEditorContextKeys())); - - this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorContextKeys())); - this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorContextKeys())); - this._register(this.editorGroupService.onDidGroupIndexChange(() => this.updateEditorContextKeys())); - - this._register(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(), true)); - - this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey())); - this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.updateWorkspaceFolderCountContextKey())); - - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) { - this.updateSplitEditorsVerticallyContext(); - } - })); - - this._register(this.layoutService.onZenModeChange(enabled => this.inZenModeContext.set(enabled))); - this._register(this.layoutService.onFullscreenChange(fullscreen => this.isFullscreenContext.set(fullscreen))); - this._register(this.layoutService.onCenteredLayoutChange(centered => this.isCenteredLayoutContext.set(centered))); - this._register(this.layoutService.onPanelPositionChange(position => this.panelPositionContext.set(position))); - - this._register(this.viewletService.onDidViewletClose(() => this.updateSideBarContextKeys())); - this._register(this.viewletService.onDidViewletOpen(() => this.updateSideBarContextKeys())); - } - - private initContextKeys(): void { // Platform IsMacContext.bindTo(this.contextKeyService); @@ -187,6 +152,38 @@ export class WorkbenchContextKeysHandler extends Disposable { // Panel Position this.panelPositionContext = PanelPositionContext.bindTo(this.contextKeyService); this.panelPositionContext.set(this.layoutService.getPanelPosition() === Position.RIGHT ? 'right' : 'bottom'); + + this.registerListeners(); + } + + private registerListeners(): void { + this.editorGroupService.whenRestored.then(() => this.updateEditorContextKeys()); + + this._register(this.editorService.onDidActiveEditorChange(() => this.updateEditorContextKeys())); + this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateEditorContextKeys())); + + this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorContextKeys())); + this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorContextKeys())); + this._register(this.editorGroupService.onDidGroupIndexChange(() => this.updateEditorContextKeys())); + + this._register(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(), true)); + + this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey())); + this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.updateWorkspaceFolderCountContextKey())); + + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) { + this.updateSplitEditorsVerticallyContext(); + } + })); + + this._register(this.layoutService.onZenModeChange(enabled => this.inZenModeContext.set(enabled))); + this._register(this.layoutService.onFullscreenChange(fullscreen => this.isFullscreenContext.set(fullscreen))); + this._register(this.layoutService.onCenteredLayoutChange(centered => this.isCenteredLayoutContext.set(centered))); + this._register(this.layoutService.onPanelPositionChange(position => this.panelPositionContext.set(position))); + + this._register(this.viewletService.onDidViewletClose(() => this.updateSideBarContextKeys())); + this._register(this.viewletService.onDidViewletOpen(() => this.updateSideBarContextKeys())); } private updateEditorContextKeys(): void { diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 005a025aa9b..db31b88fadb 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -243,11 +243,13 @@ export class ResourcesDropHandler { } // Resolve the contents of the dropped dirty resource from source - try { - const content = await this.backupFileService.resolveBackupContent((droppedDirtyEditor.backupResource!)); - await this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true)); - } catch (e) { - // Ignore error + if (droppedDirtyEditor.backupResource) { + try { + const content = await this.backupFileService.resolveBackupContent((droppedDirtyEditor.backupResource)); + await this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true)); + } catch (e) { + // Ignore error + } } return false; diff --git a/src/vs/workbench/browser/editor.ts b/src/vs/workbench/browser/editor.ts index a8b4de85142..d441fb2f0d9 100644 --- a/src/vs/workbench/browser/editor.ts +++ b/src/vs/workbench/browser/editor.ts @@ -8,6 +8,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Registry } from 'vs/platform/registry/common/platform'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { find } from 'vs/base/common/arrays'; export interface IEditorDescriptor { instantiate(instantiationService: IInstantiationService): BaseEditor; @@ -140,13 +141,7 @@ class EditorRegistry implements IEditorRegistry { } getEditorById(editorId: string): EditorDescriptor | undefined { - for (const editor of this.editors) { - if (editor.getId() === editorId) { - return editor; - } - } - - return undefined; + return find(this.editors, editor => editor.getId() === editorId); } getEditors(): readonly EditorDescriptor[] { diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 2b5d9be8790..899604797c2 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -211,7 +211,7 @@ export class ResourceLabel extends ResourceLabels { constructor( container: HTMLElement, - options: IIconLabelCreationOptions, + options: IIconLabelCreationOptions | undefined, @IInstantiationService instantiationService: IInstantiationService, @IExtensionService extensionService: IExtensionService, @IConfigurationService configurationService: IConfigurationService, @@ -252,7 +252,7 @@ class ResourceLabelWidget extends IconLabel { constructor( container: HTMLElement, - options: IIconLabelCreationOptions, + options: IIconLabelCreationOptions | undefined, @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService, @IDecorationsService private readonly decorationsService: IDecorationsService, @@ -434,7 +434,7 @@ class ResourceLabelWidget extends IconLabel { return; } - const iconLabelOptions: IIconLabelValueOptions = { + const iconLabelOptions: IIconLabelValueOptions & { extraClasses: string[] } = { title: '', italic: this.options && this.options.italic, matches: this.options && this.options.matches, @@ -462,7 +462,7 @@ class ResourceLabelWidget extends IconLabel { } if (this.options && this.options.extraClasses) { - iconLabelOptions.extraClasses!.push(...this.options.extraClasses); + iconLabelOptions.extraClasses.push(...this.options.extraClasses); } if (this.options && this.options.fileDecorations && resource) { @@ -477,11 +477,11 @@ class ResourceLabelWidget extends IconLabel { } if (this.options.fileDecorations.colors) { - iconLabelOptions.extraClasses!.push(deco.labelClassName); + iconLabelOptions.extraClasses.push(deco.labelClassName); } if (this.options.fileDecorations.badges) { - iconLabelOptions.extraClasses!.push(deco.badgeClassName); + iconLabelOptions.extraClasses.push(deco.badgeClassName); } } } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 883b7ca4cbe..be57658a338 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -36,6 +36,8 @@ import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/a import { IFileService } from 'vs/platform/files/common/files'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { coalesce } from 'vs/base/common/arrays'; +import { assertIsDefined } from 'vs/base/common/types'; +import { INotificationService, NotificationsFilter } from 'vs/platform/notification/common/notification'; enum Settings { MENUBAR_VISIBLE = 'window.menuBarVisibility', @@ -134,6 +136,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private viewletService: IViewletService; private contextService: IWorkspaceContextService; private backupFileService: IBackupFileService; + private notificationService: INotificationService; protected readonly state = { fullscreen: false, @@ -181,7 +184,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi transitionedToCenteredEditorLayout: false, wasSideBarVisible: false, wasPanelVisible: false, - transitionDisposables: new DisposableStore() + transitionDisposables: new DisposableStore(), + setNotificationsFilter: false }, }; @@ -209,6 +213,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.panelService = accessor.get(IPanelService); this.viewletService = accessor.get(IViewletService); this.titleService = accessor.get(ITitleService); + this.notificationService = accessor.get(INotificationService); accessor.get(IStatusbarService); // not used, but called to ensure instantiated accessor.get(IActivityBarService); // not used, but called to ensure instantiated @@ -340,10 +345,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.sideBar.position = position; // Adjust CSS - removeClass(activityBar.getContainer(), oldPositionValue); - removeClass(sideBar.getContainer(), oldPositionValue); - addClass(activityBar.getContainer(), newPositionValue); - addClass(sideBar.getContainer(), newPositionValue); + const activityBarContainer = assertIsDefined(activityBar.getContainer()); + const sideBarContainer = assertIsDefined(sideBar.getContainer()); + removeClass(activityBarContainer, oldPositionValue); + removeClass(sideBarContainer, oldPositionValue); + addClass(activityBarContainer, newPositionValue); + addClass(sideBarContainer, newPositionValue); // Update Styles activityBar.updateStyles(); @@ -528,10 +535,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const container = this.getContainer(part); - return isAncestor(activeElement, container); + return !!container && isAncestor(activeElement, container); } - getContainer(part: Parts): HTMLElement { + getContainer(part: Parts): HTMLElement | undefined { switch (part) { case Parts.TITLEBAR_PART: return this.getPart(Parts.TITLEBAR_PART).getContainer(); @@ -579,7 +586,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return true; // any other part cannot be hidden } - getDimension(part: Parts): Dimension { + getDimension(part: Parts): Dimension | undefined { return this.getPart(part).dimension; } @@ -647,6 +654,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi hideActivityBar: boolean; hideStatusBar: boolean; hideLineNumbers: boolean; + silentNotifications: boolean; } = this.configurationService.getValue('zenMode'); toggleFullScreen = !this.state.fullscreen && config.fullScreen; @@ -676,6 +684,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: false })); } + this.state.zenMode.setNotificationsFilter = config.silentNotifications; + if (config.silentNotifications) { + this.notificationService.setFilter(NotificationsFilter.SILENT); + } + if (config.centerLayout) { this.centerEditorLayout(true, true); } @@ -701,6 +714,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.doUpdateLayoutConfiguration(true); this.editorGroupService.activeGroup.focus(); + if (this.state.zenMode.setNotificationsFilter) { + this.notificationService.setFilter(NotificationsFilter.OFF); + } toggleFullScreen = this.state.zenMode.transitionedToFullScreen && this.state.fullscreen; } @@ -1121,8 +1137,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.storageService.store(Storage.PANEL_POSITION, positionToString(this.state.panel.position), StorageScope.WORKSPACE); // Adjust CSS - removeClass(panelPart.getContainer(), oldPositionValue); - addClass(panelPart.getContainer(), newPositionValue); + const panelContainer = assertIsDefined(panelPart.getContainer()); + removeClass(panelContainer, oldPositionValue); + addClass(panelContainer, newPositionValue); // Update Styles panelPart.updateStyles(); @@ -1161,7 +1178,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width); const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, workbenchDimensions.height); // At some point, we will not fall back to old keys from legacy layout, but for now, let's migrate the keys - const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300))!); + const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300))); const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, this.storageService.getNumber(this.state.panel.position === Position.BOTTOM ? 'workbench.panel.height' : 'workbench.panel.width', StorageScope.GLOBAL, workbenchDimensions.height / 3)); const titleBarHeight = this.titleBarPartView.minimumHeight; diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index 3e1a33fbe2c..b4cbfb337cc 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -11,6 +11,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; import { isAncestor } from 'vs/base/browser/dom'; +import { assertIsDefined } from 'vs/base/common/types'; export abstract class Panel extends Composite implements IPanel { } @@ -25,7 +26,7 @@ export class PanelDescriptor extends CompositeDescriptor { } export class PanelRegistry extends CompositeRegistry { - private defaultPanelId!: string; + private defaultPanelId: string | undefined; /** * Registers a panel to the platform. @@ -44,7 +45,7 @@ export class PanelRegistry extends CompositeRegistry { /** * Returns a panel by id. */ - getPanel(id: string): PanelDescriptor | null { + getPanel(id: string): PanelDescriptor | undefined { return this.getComposite(id); } @@ -66,7 +67,7 @@ export class PanelRegistry extends CompositeRegistry { * Gets the id of the panel that should open on startup by default. */ getDefaultPanelId(): string { - return this.defaultPanelId; + return assertIsDefined(this.defaultPanelId); } /** @@ -111,8 +112,9 @@ export abstract class TogglePanelAction extends Action { private isPanelFocused(): boolean { const activeElement = document.activeElement; + const panelPart = this.layoutService.getContainer(Parts.PANEL_PART); - return !!(this.isPanelActive() && activeElement && isAncestor(activeElement, this.layoutService.getContainer(Parts.PANEL_PART))); + return !!(this.isPanelActive() && activeElement && panelPart && isAncestor(activeElement, panelPart)); } } diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index 80e0780610f..241d86e34ac 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -12,6 +12,7 @@ import { IDimension } from 'vs/platform/layout/browser/layoutService'; import { ISerializableView, IViewSize } from 'vs/base/browser/ui/grid/grid'; import { Event, Emitter } from 'vs/base/common/event'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { assertIsDefined } from 'vs/base/common/types'; export interface IPartOptions { hasTitle?: boolean; @@ -29,13 +30,13 @@ export interface ILayoutContentResult { */ export abstract class Part extends Component implements ISerializableView { - private _dimension: Dimension; - get dimension(): Dimension { return this._dimension; } + private _dimension: Dimension | undefined; + get dimension(): Dimension | undefined { return this._dimension; } - private parent: HTMLElement; - private titleArea: HTMLElement | null = null; - private contentArea: HTMLElement | null = null; - private partLayout: PartLayout; + private parent: HTMLElement | undefined; + private titleArea: HTMLElement | undefined; + private contentArea: HTMLElement | undefined; + private partLayout: PartLayout | undefined; constructor( id: string, @@ -80,35 +81,35 @@ export abstract class Part extends Component implements ISerializableView { /** * Returns the overall part container. */ - getContainer(): HTMLElement { + getContainer(): HTMLElement | undefined { return this.parent; } /** * Subclasses override to provide a title area implementation. */ - protected createTitleArea(parent: HTMLElement, options?: object): HTMLElement | null { - return null; + protected createTitleArea(parent: HTMLElement, options?: object): HTMLElement | undefined { + return undefined; } /** * Returns the title area container. */ - protected getTitleArea(): HTMLElement | null { + protected getTitleArea(): HTMLElement | undefined { return this.titleArea; } /** * Subclasses override to provide a content area implementation. */ - protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | null { - return null; + protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | undefined { + return undefined; } /** * Returns the content area container. */ - protected getContentArea(): HTMLElement | null { + protected getContentArea(): HTMLElement | undefined { return this.contentArea; } @@ -116,7 +117,9 @@ export abstract class Part extends Component implements ISerializableView { * Layout title and content area in the given dimension. */ protected layoutContents(width: number, height: number): ILayoutContentResult { - return this.partLayout.layout(width, height); + const partLayout = assertIsDefined(this.partLayout); + + return partLayout.layout(width, height); } //#region ISerializableView @@ -144,7 +147,7 @@ class PartLayout { private static readonly TITLE_HEIGHT = 35; - constructor(private options: IPartOptions, private contentArea: HTMLElement | null) { } + constructor(private options: IPartOptions, private contentArea: HTMLElement | undefined) { } layout(width: number, height: number): ILayoutContentResult { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 4757e8efca6..37f757732cd 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -22,7 +22,7 @@ import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarCol import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { IActivity } from 'vs/workbench/common/activity'; -import { ACTIVITY_BAR_FOREGROUND } from 'vs/workbench/common/theme'; +import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -163,15 +163,19 @@ export class GlobalActivityActionViewItem extends ActivityActionViewItem { export class PlaceHolderViewletActivityAction extends ViewletActivityAction { constructor( - id: string, name: string, iconUrl: URI, + id: string, + name: string, + iconUrl: URI | undefined, @IViewletService viewletService: IViewletService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @ITelemetryService telemetryService: ITelemetryService ) { super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, layoutService, telemetryService); - const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar - DOM.createCSSRule(iconClass, `-webkit-mask: ${DOM.asCSSUrl(iconUrl)} no-repeat 50% 50%; -webkit-mask-size: 24px;`); + if (iconUrl) { + const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar + DOM.createCSSRule(iconClass, `-webkit-mask: ${DOM.asCSSUrl(iconUrl)} no-repeat 50% 50%; -webkit-mask-size: 24px;`); + } } setActivity(activity: IActivity): void { @@ -222,7 +226,7 @@ class SwitchSideBarViewAction extends Action { export class PreviousSideBarViewAction extends SwitchSideBarViewAction { static readonly ID = 'workbench.action.previousSideBarView'; - static LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View'); + static readonly LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View'); constructor( id: string, @@ -241,7 +245,7 @@ export class PreviousSideBarViewAction extends SwitchSideBarViewAction { export class NextSideBarViewAction extends SwitchSideBarViewAction { static readonly ID = 'workbench.action.nextSideBarView'; - static LABEL = nls.localize('nextSideBarView', 'Next Side Bar View'); + static readonly LABEL = nls.localize('nextSideBarView', 'Next Side Bar View'); constructor( id: string, @@ -274,6 +278,25 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { `); } + const activeBorderColor = theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER); + if (activeBorderColor) { + collector.addRule(` + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before { + border-left-color: ${activeBorderColor}; + } + `); + } + + const activeBackgroundColor = theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND); + if (activeBackgroundColor) { + collector.addRule(` + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator { + z-index: 0; + background-color: ${activeBackgroundColor}; + } + `); + } + // Styling with Outline color (e.g. high contrast theme) const outline = theme.getColor(activeContrastBorder); if (outline) { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index e98dc753597..a6ecbcaeb4d 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/activitybarpart'; import * as nls from 'vs/nls'; -import { illegalArgument } from 'vs/base/common/errors'; import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -15,10 +14,10 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; -import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; +import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar, ICompositeBarItem } from 'vs/workbench/browser/parts/compositeBar'; import { Dimension, addClass, removeNode } from 'vs/base/browser/dom'; @@ -30,7 +29,7 @@ import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { IViewsService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection } from 'vs/workbench/common/views'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IViewlet } from 'vs/workbench/common/viewlet'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Schemas } from 'vs/base/common/network'; @@ -65,17 +64,17 @@ export class ActivitybarPart extends Part implements IActivityBarService { //#endregion - private globalActivityAction: ActivityAction; - private globalActivityActionBar: ActionBar; + private globalActivityAction: ActivityAction | undefined; + private globalActivityActionBar: ActionBar | undefined; private customMenubar: CustomMenubarControl | undefined; private menubar: HTMLElement | undefined; - private content: HTMLElement; + private content: HTMLElement | undefined; private cachedViewlets: ICachedViewlet[] = []; private compositeBar: CompositeBar; - private compositeActions: Map = new Map(); + private readonly compositeActions: Map = new Map(); private readonly viewletDisposables: Map = new Map(); @@ -110,7 +109,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { openComposite: (compositeId: string) => this.viewletService.openViewlet(compositeId, true), getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction, getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction, - getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, this.viewletService.getViewlet(compositeId)), + getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, assertIsDefined(this.viewletService.getViewlet(compositeId))), getContextMenuActions: () => [this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))], getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(), hidePart: () => this.layoutService.setSideBarHidden(true), @@ -169,7 +168,9 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (foundViewlet) { this.compositeBar.addComposite(foundViewlet); } + this.compositeBar.activateComposite(viewlet.getId()); + const viewletDescriptor = this.viewletService.getViewlet(viewlet.getId()); if (viewletDescriptor) { const viewContainer = this.getViewContainer(viewletDescriptor.id); @@ -191,13 +192,15 @@ export class ActivitybarPart extends Part implements IActivityBarService { return this.showGlobalActivity(badge, clazz); } - throw illegalArgument('globalActivityId'); + return Disposable.None; } private showGlobalActivity(badge: IBadge, clazz?: string): IDisposable { - this.globalActivityAction.setBadge(badge, clazz); + const globalActivityAction = assertIsDefined(this.globalActivityAction); - return toDisposable(() => this.globalActivityAction.setBadge(undefined)); + globalActivityAction.setBadge(badge, clazz); + + return toDisposable(() => globalActivityAction.setBadge(undefined)); } private uninstallMenubar() { @@ -213,12 +216,13 @@ export class ActivitybarPart extends Part implements IActivityBarService { private installMenubar() { this.menubar = document.createElement('div'); addClass(this.menubar, 'menubar'); - this.content.prepend(this.menubar); + + const content = assertIsDefined(this.content); + content.prepend(this.menubar); // Menubar: install a custom menu bar depending on configuration this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); this.customMenubar.create(this.menubar); - } createContentArea(parent: HTMLElement): HTMLElement { @@ -243,7 +247,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.createGlobalActivityActionBar(globalActivities); - this.element.style.display = this.layoutService.isVisible(Parts.ACTIVITYBAR_PART) ? null : 'none'; + this.element.style.display = this.layoutService.isVisible(Parts.ACTIVITYBAR_PART) ? '' : 'none'; return this.content; } @@ -252,25 +256,27 @@ export class ActivitybarPart extends Part implements IActivityBarService { super.updateStyles(); // Part container - const container = this.getContainer(); - const background = this.getColor(ACTIVITY_BAR_BACKGROUND); + const container = assertIsDefined(this.getContainer()); + const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || ''; container.style.backgroundColor = background; - const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder); + const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || ''; const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT; container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : ''; - container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null; - container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null; - container.style.borderRightColor = isPositionLeft ? borderColor : null; - container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : null; - container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : null; - container.style.borderLeftColor = !isPositionLeft ? borderColor : null; + container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : ''; + container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : ''; + container.style.borderRightColor = isPositionLeft ? borderColor : ''; + container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : ''; + container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : ''; + container.style.borderLeftColor = !isPositionLeft ? borderColor : ''; } private getActivitybarItemColors(theme: ITheme): ICompositeBarColors { return { activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND), inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND), + activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER), + activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND), badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), dragAndDropBackground: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND), @@ -280,7 +286,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private createGlobalActivityActionBar(container: HTMLElement): void { this.globalActivityActionBar = this._register(new ActionBar(container, { - actionViewItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionViewItem, a, (theme: ITheme) => this.getActivitybarItemColors(theme)), + actionViewItemProvider: action => this.instantiationService.createInstance(GlobalActivityActionViewItem, action, (theme: ITheme) => this.getActivitybarItemColors(theme)), orientation: ActionsOrientation.VERTICAL, ariaLabel: nls.localize('manage', "Manage"), animated: false @@ -291,6 +297,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { name: nls.localize('manage', "Manage"), cssClass: 'update-activity' }); + this.globalActivityActionBar.push(this.globalActivityAction); } @@ -336,6 +343,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } } } + for (const viewlet of viewlets) { this.enableCompositeActions(viewlet); const viewContainer = this.getViewContainer(viewlet.id); @@ -354,6 +362,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (disposable) { disposable.dispose(); } + this.viewletDisposables.delete(viewletId); this.hideComposite(viewletId); } @@ -371,6 +380,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (!viewContainer || !viewContainer.hideIfEmpty) { return false; } + return cachedViewlet && cachedViewlet.views && cachedViewlet.views.length ? cachedViewlet.views.every(({ when }) => !!when && !this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(when))) : viewletId === TEST_VIEW_CONTAINER_ID /* Hide Test viewlet for the first time or it had no views registered before */; @@ -387,6 +397,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private hideComposite(compositeId: string): void { this.compositeBar.hideComposite(compositeId); + const compositeActions = this.compositeActions.get(compositeId); if (compositeActions) { compositeActions.activityAction.dispose(); @@ -417,7 +428,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { setVisible(visible: boolean): void { if (this.element) { - this.element.style.display = visible ? null : 'none'; + this.element.style.display = visible ? '' : 'none'; } } @@ -440,7 +451,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { if (e.key === ActivitybarPart.PINNED_VIEWLETS && e.scope === StorageScope.GLOBAL && this.cachedViewletsValue !== this.getStoredCachedViewletsValue() /* This checks if current window changed the value or not */) { - this._cachedViewletsValue = null; + this._cachedViewletsValue = undefined; const newCompositeItems: ICompositeBarItem[] = []; const compositeItems = this.compositeBar.getCompositeBarItems(); const cachedViewlets = this.getCachedViewlets(); @@ -525,7 +536,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { return result; } - private _cachedViewletsValue: string | null; + private _cachedViewletsValue: string | undefined; private get cachedViewletsValue(): string { if (!this._cachedViewletsValue) { this._cachedViewletsValue = this.getStoredCachedViewletsValue(); diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 72c433c9a67..eaa4f1af87a 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -10,6 +10,8 @@ } .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label { + position: relative; + z-index: 1; display: flex; overflow: hidden; height: 40px; @@ -20,19 +22,41 @@ font-size: 15px; } +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before { content: ""; position: absolute; top: 9px; height: 32px; + z-index: 1; + top: 5px; + height: 40px; width: 0; border-left: 2px solid; } +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before { + top: 0; + height: 100%; +} + +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:focus .active-item-indicator:before { + border-left: none; /* don't show active border + focus at the same time, focus takes priority */ +} + +/* Hides active elements in high contrast mode */ +.hc-black .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator { + display: none; +} + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.clicked:focus:before { border-left: none !important; /* no focus feedback when using mouse */ } +.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before{ + left: 0; +} + .monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before { left: 1px; } @@ -41,8 +65,20 @@ right: 1px; } +.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before { + right: 2px; +} + +/* Hides outline on HC as focus is handled by border */ +.hc-black .monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before, +.hc-black .monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before { + outline: none; +} + +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .active-item-indicator, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge { position: absolute; + z-index: 1; top: 5px; left: 0; overflow: hidden; diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 7a39fce8178..33495bbc8de 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -48,7 +48,7 @@ export class CompositeBar extends Widget implements ICompositeBar { private dimension: Dimension | undefined; - private compositeSwitcherBar!: ActionBar; + private compositeSwitcherBar: ActionBar | undefined; private compositeOverflowAction: CompositeOverflowActivityAction | undefined; private compositeOverflowActionViewItem: CompositeOverflowActivityActionViewItem | undefined; @@ -92,6 +92,7 @@ export class CompositeBar extends Widget implements ICompositeBar { create(parent: HTMLElement): HTMLElement { const actionBarDiv = parent.appendChild($('.composite-bar')); + this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, { actionViewItemProvider: (action: Action) => { if (action instanceof CompositeOverflowActivityAction) { @@ -113,12 +114,15 @@ export class CompositeBar extends Widget implements ICompositeBar { if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { EventHelper.stop(e, true); - const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id; - this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); + const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype); + if (Array.isArray(data)) { + const draggedCompositeId = data[0].id; + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); - const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1]; - if (targetItem && targetItem.id !== draggedCompositeId) { - this.move(draggedCompositeId, targetItem.id); + const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1]; + if (targetItem && targetItem.id !== draggedCompositeId) { + this.move(draggedCompositeId, targetItem.id); + } } } })); @@ -278,21 +282,23 @@ export class CompositeBar extends Widget implements ICompositeBar { if (size) { items.forEach(composite => this.compositeSizeInBar.set(composite.id, size)); } else { - if (this.dimension && this.dimension.height !== 0 && this.dimension.width !== 0) { + const compositeSwitcherBar = this.compositeSwitcherBar; + if (compositeSwitcherBar && this.dimension && this.dimension.height !== 0 && this.dimension.width !== 0) { // Compute sizes only if visible. Otherwise the size measurment would be computed wrongly. - const currentItemsLength = this.compositeSwitcherBar.viewItems.length; - this.compositeSwitcherBar.push(items.map(composite => composite.activityAction)); + const currentItemsLength = compositeSwitcherBar.viewItems.length; + compositeSwitcherBar.push(items.map(composite => composite.activityAction)); items.map((composite, index) => this.compositeSizeInBar.set(composite.id, this.options.orientation === ActionsOrientation.VERTICAL - ? this.compositeSwitcherBar.getHeight(currentItemsLength + index) - : this.compositeSwitcherBar.getWidth(currentItemsLength + index) + ? compositeSwitcherBar.getHeight(currentItemsLength + index) + : compositeSwitcherBar.getWidth(currentItemsLength + index) )); - items.forEach(() => this.compositeSwitcherBar.pull(this.compositeSwitcherBar.viewItems.length - 1)); + items.forEach(() => compositeSwitcherBar.pull(compositeSwitcherBar.viewItems.length - 1)); } } } private updateCompositeSwitcher(): void { - if (!this.compositeSwitcherBar || !this.dimension) { + const compositeSwitcherBar = this.compositeSwitcherBar; + if (!compositeSwitcherBar || !this.dimension) { return; // We have not been rendered yet so there is nothing to update. } @@ -340,7 +346,7 @@ export class CompositeBar extends Widget implements ICompositeBar { // Pull out overflow action if there is a composite change so that we can add it to the end later if (this.compositeOverflowAction && visibleCompositesChange) { - this.compositeSwitcherBar.pull(this.compositeSwitcherBar.length() - 1); + compositeSwitcherBar.pull(compositeSwitcherBar.length() - 1); this.compositeOverflowAction.dispose(); this.compositeOverflowAction = undefined; @@ -359,8 +365,8 @@ export class CompositeBar extends Widget implements ICompositeBar { } }); compositesToRemove.reverse().forEach(index => { - const actionViewItem = this.compositeSwitcherBar.viewItems[index]; - this.compositeSwitcherBar.pull(index); + const actionViewItem = compositeSwitcherBar.viewItems[index]; + compositeSwitcherBar.pull(index); actionViewItem.dispose(); this.visibleComposites.splice(index, 1); }); @@ -370,19 +376,19 @@ export class CompositeBar extends Widget implements ICompositeBar { const currentIndex = this.visibleComposites.indexOf(compositeId); if (newIndex !== currentIndex) { if (currentIndex !== -1) { - const actionViewItem = this.compositeSwitcherBar.viewItems[currentIndex]; - this.compositeSwitcherBar.pull(currentIndex); + const actionViewItem = compositeSwitcherBar.viewItems[currentIndex]; + compositeSwitcherBar.pull(currentIndex); actionViewItem.dispose(); this.visibleComposites.splice(currentIndex, 1); } - this.compositeSwitcherBar.push(this.model.findItem(compositeId).activityAction, { label: true, icon: this.options.icon, index: newIndex }); + compositeSwitcherBar.push(this.model.findItem(compositeId).activityAction, { label: true, icon: this.options.icon, index: newIndex }); this.visibleComposites.splice(newIndex, 0, compositeId); } }); // Add overflow action as needed - if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) { + if ((visibleCompositesChange && overflows) || compositeSwitcherBar.length() === 0) { this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => { if (this.compositeOverflowActionViewItem) { this.compositeOverflowActionViewItem.showMenu(); @@ -401,7 +407,7 @@ export class CompositeBar extends Widget implements ICompositeBar { this.options.colors ); - this.compositeSwitcherBar.push(this.compositeOverflowAction, { label: false, icon: true }); + compositeSwitcherBar.push(this.compositeOverflowAction, { label: false, icon: true }); } this._onDidChange.fire(); diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 33c609eba58..88f5a78a1da 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -57,7 +57,7 @@ export class ActivityAction extends Action { private readonly _onDidChangeBadge = new Emitter(); readonly onDidChangeBadge: Event = this._onDidChangeBadge.event; - private badge?: IBadge; + private badge: IBadge | undefined; private clazz: string | undefined; constructor(private _activity: IActivity) { @@ -110,6 +110,8 @@ export class ActivityAction extends Action { export interface ICompositeBarColors { activeBackgroundColor?: Color; inactiveBackgroundColor?: Color; + activeBorderColor?: Color; + activeBackground?: Color; activeBorderBottomColor?: Color; activeForegroundColor?: Color; inactiveForegroundColor?: Color; @@ -124,10 +126,10 @@ export interface IActivityActionViewItemOptions extends IBaseActionViewItemOptio } export class ActivityActionViewItem extends BaseActionViewItem { - protected container!: HTMLElement; - protected label!: HTMLElement; - protected badge!: HTMLElement; - protected options!: IActivityActionViewItemOptions; + protected container: HTMLElement; + protected label: HTMLElement; + protected badge: HTMLElement; + protected options: IActivityActionViewItemOptions; private badgeContent: HTMLElement | undefined; private readonly badgeDisposable = this._register(new MutableDisposable()); @@ -156,12 +158,12 @@ export class ActivityActionViewItem extends BaseActionViewItem { if (this.label) { if (this.options.icon) { const foreground = this._action.checked ? colors.activeBackgroundColor || colors.activeForegroundColor : colors.inactiveBackgroundColor || colors.inactiveForegroundColor; - this.label.style.backgroundColor = foreground ? foreground.toString() : null; + this.label.style.backgroundColor = foreground ? foreground.toString() : ''; } else { const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor; const borderBottomColor = this._action.checked ? colors.activeBorderBottomColor : null; this.label.style.color = foreground ? foreground.toString() : null; - this.label.style.borderBottomColor = borderBottomColor ? borderBottomColor.toString() : null; + this.label.style.borderBottomColor = borderBottomColor ? borderBottomColor.toString() : ''; } } @@ -172,11 +174,11 @@ export class ActivityActionViewItem extends BaseActionViewItem { const contrastBorderColor = theme.getColor(contrastBorder); this.badgeContent.style.color = badgeForeground ? badgeForeground.toString() : null; - this.badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : null; + this.badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : ''; - this.badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : null; - this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : null; - this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : null; + this.badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : ''; + this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : ''; + this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : ''; } } @@ -205,12 +207,19 @@ export class ActivityActionViewItem extends BaseActionViewItem { })); // Label - this.label = dom.append(this.element!, dom.$('a')); + this.label = dom.append(container, dom.$('a')); // Badge - this.badge = dom.append(this.element!, dom.$('.badge')); + this.badge = dom.append(container, dom.$('.badge')); this.badgeContent = dom.append(this.badge, dom.$('.badge-content')); + // Activity bar active border + background + const isActivityBarItem = this.options.icon; + if (isActivityBarItem) { + dom.append(container, dom.$('.active-item-indicator')); + } + + dom.hide(this.badge); this.updateActivity(); @@ -347,7 +356,7 @@ export class CompositeOverflowActivityAction extends ActivityAction { } export class CompositeOverflowActivityActionViewItem extends ActivityActionViewItem { - private actions: Action[] | undefined; + private actions: Action[] = []; constructor( action: ActivityAction, @@ -370,9 +379,9 @@ export class CompositeOverflowActivityActionViewItem extends ActivityActionViewI this.actions = this.getActions(); this.contextMenuService.showContextMenu({ - getAnchor: () => this.element!, - getActions: () => this.actions!, - onHide: () => dispose(this.actions!) + getAnchor: () => this.container, + getActions: () => this.actions, + onHide: () => dispose(this.actions) }); } @@ -439,7 +448,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem { constructor( private compositeActivityAction: ActivityAction, private toggleCompositePinnedAction: Action, - private contextMenuActionsProvider: () => Action[], + private contextMenuActionsProvider: () => ReadonlyArray, colors: (theme: ITheme) => ICompositeBarColors, icon: boolean, private compositeBar: ICompositeBar, @@ -502,7 +511,9 @@ export class CompositeActionViewItem extends ActivityActionViewItem { // Allow to drag this._register(dom.addDisposableListener(this.container, dom.EventType.DRAG_START, (e: DragEvent) => { - e.dataTransfer!.effectAllowed = 'move'; + if (e.dataTransfer) { + e.dataTransfer.effectAllowed = 'move'; + } // Registe as dragged to local transfer this.compositeTransfer.setData([new DraggedCompositeIdentifier(this.activity.id)], DraggedCompositeIdentifier.prototype); @@ -515,8 +526,11 @@ export class CompositeActionViewItem extends ActivityActionViewItem { this._register(new DragAndDropObserver(this.container, { onDragEnter: e => { - if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id !== this.activity.id) { - this.updateFromDragging(container, true); + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { + const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype); + if (Array.isArray(data) && data[0].id !== this.activity.id) { + this.updateFromDragging(container, true); + } } }, @@ -538,12 +552,15 @@ export class CompositeActionViewItem extends ActivityActionViewItem { dom.EventHelper.stop(e, true); if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { - const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id; - if (draggedCompositeId !== this.activity.id) { - this.updateFromDragging(container, false); - this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); + const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype); + if (Array.isArray(data)) { + const draggedCompositeId = data[0].id; + if (draggedCompositeId !== this.activity.id) { + this.updateFromDragging(container, false); + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); - this.compositeBar.move(draggedCompositeId, this.activity.id); + this.compositeBar.move(draggedCompositeId, this.activity.id); + } } } } @@ -563,7 +580,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem { const theme = this.themeService.getTheme(); const dragBackground = this.options.colors(theme).dragAndDropBackground; - element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null; + element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : ''; } private showContextMenu(container: HTMLElement): void { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index adac882f1d3..fbc33409d13 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -32,6 +32,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Dimension, append, $, addClass, hide, show, addClasses } from 'vs/base/browser/dom'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { assertIsDefined } from 'vs/base/common/types'; export interface ICompositeTitleLabel { @@ -57,15 +58,15 @@ export abstract class CompositePart extends Part { protected readonly onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>()); protected readonly onDidCompositeClose = this._register(new Emitter()); - protected toolBar!: ToolBar; + protected toolBar: ToolBar | undefined; private mapCompositeToCompositeContainer = new Map(); private mapActionsBindingToComposite = new Map void>(); - private activeComposite: Composite | null; + private activeComposite: Composite | undefined; private lastActiveCompositeId: string; private instantiatedCompositeItems: Map; - private titleLabel!: ICompositeTitleLabel; - private progressBar!: ProgressBar; + private titleLabel: ICompositeTitleLabel | undefined; + private progressBar: ProgressBar | undefined; private contentAreaSize: Dimension | undefined; private readonly telemetryActionsListener = this._register(new MutableDisposable()); private currentCompositeOpenToken: string | undefined; @@ -90,7 +91,6 @@ export abstract class CompositePart extends Part { ) { super(id, options, themeService, storageService, layoutService); - this.activeComposite = null; this.instantiatedCompositeItems = new Map(); this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId); } @@ -168,7 +168,7 @@ export abstract class CompositePart extends Part { // Instantiate composite from registry otherwise const compositeDescriptor = this.registry.getComposite(id); if (compositeDescriptor) { - const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, this.progressBar, compositeDescriptor.id, isActive); + const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, this.progressBar, compositeDescriptor.id, !!isActive); const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection( [IEditorProgressService, compositeProgressIndicator] // provide the editor progress service for any editors instantiated within the composite )); @@ -234,7 +234,8 @@ export abstract class CompositePart extends Part { show(compositeContainer); // Setup action runner - this.toolBar.actionRunner = composite.getActionRunner(); + const toolBar = assertIsDefined(this.toolBar); + toolBar.actionRunner = composite.getActionRunner(); // Update title with composite title if it differs from descriptor const descriptor = this.registry.getComposite(composite.getId()); @@ -251,7 +252,7 @@ export abstract class CompositePart extends Part { actionsBinding(); // Action Run Handling - this.telemetryActionsListener.value = this.toolBar.actionRunner.onDidRun(e => { + this.telemetryActionsListener.value = toolBar.actionRunner.onDidRun(e => { // Check for Error if (e.error && !errors.isPromiseCanceledError(e.error)) { @@ -312,7 +313,8 @@ export abstract class CompositePart extends Part { this.titleLabel.updateTitle(compositeId, compositeTitle, (keybinding && keybinding.getLabel()) || undefined); - this.toolBar.setAriaLabel(nls.localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle)); + const toolBar = assertIsDefined(this.toolBar); + toolBar.setAriaLabel(nls.localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle)); } private collectCompositeActions(composite: Composite): () => void { @@ -326,13 +328,14 @@ export abstract class CompositePart extends Part { secondaryActions.push(...this.getSecondaryActions()); // Update context - this.toolBar.context = this.actionsContextProvider(); + const toolBar = assertIsDefined(this.toolBar); + toolBar.context = this.actionsContextProvider(); // Return fn to set into toolbar - return this.toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); + return toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); } - protected getActiveComposite(): IComposite | null { + protected getActiveComposite(): IComposite | undefined { return this.activeComposite; } @@ -346,7 +349,7 @@ export abstract class CompositePart extends Part { } const composite = this.activeComposite; - this.activeComposite = null; + this.activeComposite = undefined; const compositeContainer = this.mapCompositeToCompositeContainer.get(composite.getId()); @@ -360,10 +363,14 @@ export abstract class CompositePart extends Part { } // Clear any running Progress - this.progressBar.stop().hide(); + if (this.progressBar) { + this.progressBar.stop().hide(); + } // Empty Actions - this.toolBar.setActions([])(); + if (this.toolBar) { + this.toolBar.setActions([])(); + } this.onDidCompositeClose.fire(composite); return composite; @@ -413,7 +420,8 @@ export abstract class CompositePart extends Part { super.updateStyles(); // Forward to title label - this.titleLabel.updateStyles(); + const titleLabel = assertIsDefined(this.titleLabel); + titleLabel.updateStyles(); } protected actionViewItemProvider(action: IAction): IActionViewItem | undefined { @@ -446,10 +454,10 @@ export abstract class CompositePart extends Part { return contentContainer; } - getProgressIndicator(id: string): IProgressIndicator | null { + getProgressIndicator(id: string): IProgressIndicator | undefined { const compositeItem = this.instantiatedCompositeItems.get(id); - return compositeItem ? compositeItem.progress : null; + return compositeItem ? compositeItem.progress : undefined; } protected getActions(): ReadonlyArray { diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index 380573ef808..53b00a24b33 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -19,6 +19,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { dispose } from 'vs/base/common/lifecycle'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; export interface IOpenCallbacks { openInternal: (input: EditorInput, options: EditorOptions | undefined) => Promise; @@ -38,8 +39,8 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { private callbacks: IOpenCallbacks; private metadata: string | undefined; - private binaryContainer: HTMLElement; - private scrollbar: DomScrollableElement; + private binaryContainer: HTMLElement | undefined; + private scrollbar: DomScrollableElement | undefined; private resourceViewerContext: ResourceViewerContext | undefined; constructor( @@ -91,7 +92,8 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { this.resourceViewerContext.dispose(); } - this.resourceViewerContext = ResourceViewer.show({ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, this.binaryContainer, this.scrollbar, { + const [binaryContainer, scrollbar] = assertAllDefined(this.binaryContainer, this.scrollbar); + this.resourceViewerContext = ResourceViewer.show({ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, binaryContainer, scrollbar, { openInternalClb: () => this.handleOpenInternalCallback(input, options), openExternalClb: this.environmentService.configuration.remoteAuthority ? undefined : resource => this.callbacks.openExternal(resource), metadataClb: meta => this.handleMetadataChanged(meta) @@ -120,8 +122,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { // Clear Meta this.handleMetadataChanged(undefined); - // Clear Resource Viewer - clearNode(this.binaryContainer); + // Clear the rest + if (this.binaryContainer) { + clearNode(this.binaryContainer); + } dispose(this.resourceViewerContext); this.resourceViewerContext = undefined; @@ -131,19 +135,24 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { layout(dimension: Dimension): void { // Pass on to Binary Container - size(this.binaryContainer, dimension.width, dimension.height); - this.scrollbar.scanDomNode(); + const [binaryContainer, scrollbar] = assertAllDefined(this.binaryContainer, this.scrollbar); + size(binaryContainer, dimension.width, dimension.height); + scrollbar.scanDomNode(); if (this.resourceViewerContext && this.resourceViewerContext.layout) { this.resourceViewerContext.layout(dimension); } } focus(): void { - this.binaryContainer.focus(); + const binaryContainer = assertIsDefined(this.binaryContainer); + + binaryContainer.focus(); } dispose(): void { - this.binaryContainer.remove(); + if (this.binaryContainer) { + this.binaryContainer.remove(); + } dispose(this.resourceViewerContext); this.resourceViewerContext = undefined; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 96a35f5a192..fdb54c058b5 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -66,14 +66,14 @@ export abstract class BreadcrumbsConfig { // internal } - static IsEnabled = BreadcrumbsConfig._stub('breadcrumbs.enabled'); - static UseQuickPick = BreadcrumbsConfig._stub('breadcrumbs.useQuickPick'); - static FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath'); - static SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath'); - static SymbolSortOrder = BreadcrumbsConfig._stub<'position' | 'name' | 'type'>('breadcrumbs.symbolSortOrder'); - static Icons = BreadcrumbsConfig._stub('breadcrumbs.icons'); + static readonly IsEnabled = BreadcrumbsConfig._stub('breadcrumbs.enabled'); + static readonly UseQuickPick = BreadcrumbsConfig._stub('breadcrumbs.useQuickPick'); + static readonly FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath'); + static readonly SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath'); + static readonly SymbolSortOrder = BreadcrumbsConfig._stub<'position' | 'name' | 'type'>('breadcrumbs.symbolSortOrder'); + static readonly Icons = BreadcrumbsConfig._stub('breadcrumbs.icons'); - static FileExcludes = BreadcrumbsConfig._stub('files.exclude'); + static readonly FileExcludes = BreadcrumbsConfig._stub('files.exclude'); private static _stub(name: string): { bindTo(service: IConfigurationService): BreadcrumbsConfig } { return { @@ -166,6 +166,136 @@ Registry.as(Extensions.Configuration).registerConfigurat description: localize('icons', "Render breadcrumb items with icons."), type: 'boolean', default: true + }, + 'breadcrumbs.filteredTypes.file': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.file', "When set to `false` breadcrumbs never show `file`-symbols.") + }, + 'breadcrumbs.filteredTypes.module': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.module', "When set to `false` breadcrumbs never show `module`-symbols.") + }, + 'breadcrumbs.filteredTypes.namespace': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.namespace', "When set to `false` breadcrumbs never show `namespace`-symbols.") + }, + 'breadcrumbs.filteredTypes.package': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.package', "When set to `false` breadcrumbs never show `package`-symbols.") + }, + 'breadcrumbs.filteredTypes.class': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.class', "When set to `false` breadcrumbs never show `class`-symbols.") + }, + 'breadcrumbs.filteredTypes.method': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.method', "When set to `false` breadcrumbs never show `method`-symbols.") + }, + 'breadcrumbs.filteredTypes.property': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.property', "When set to `false` breadcrumbs never show `property`-symbols.") + }, + 'breadcrumbs.filteredTypes.field': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.field', "When set to `false` breadcrumbs never show `field`-symbols.") + }, + 'breadcrumbs.filteredTypes.constructor': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.constructor', "When set to `false` breadcrumbs never show `constructor`-symbols.") + }, + 'breadcrumbs.filteredTypes.enum': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.enum', "When set to `false` breadcrumbs never show `enum`-symbols.") + }, + 'breadcrumbs.filteredTypes.interface': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.interface', "When set to `false` breadcrumbs never show `interface`-symbols.") + }, + 'breadcrumbs.filteredTypes.function': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.function', "When set to `false` breadcrumbs never show `function`-symbols.") + }, + 'breadcrumbs.filteredTypes.variable': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.variable', "When set to `false` breadcrumbs never show `variable`-symbols.") + }, + 'breadcrumbs.filteredTypes.constant': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.constant', "When set to `false` breadcrumbs never show `constant`-symbols.") + }, + 'breadcrumbs.filteredTypes.string': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.string', "When set to `false` breadcrumbs never show `string`-symbols.") + }, + 'breadcrumbs.filteredTypes.number': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.number', "When set to `false` breadcrumbs never show `number`-symbols.") + }, + 'breadcrumbs.filteredTypes.boolean': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.boolean', "When set to `false` breadcrumbs never show `boolean`-symbols.") + }, + 'breadcrumbs.filteredTypes.array': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.array', "When set to `false` breadcrumbs never show `array`-symbols.") + }, + 'breadcrumbs.filteredTypes.object': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.object', "When set to `false` breadcrumbs never show `object`-symbols.") + }, + 'breadcrumbs.filteredTypes.key': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.key', "When set to `false` breadcrumbs never show `key`-symbols.") + }, + 'breadcrumbs.filteredTypes.null': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.null', "When set to `false` breadcrumbs never show `null`-symbols.") + }, + 'breadcrumbs.filteredTypes.enumMember': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.enumMember', "When set to `false` breadcrumbs never show `enumMember`-symbols.") + }, + 'breadcrumbs.filteredTypes.struct': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.struct', "When set to `false` breadcrumbs never show `struct`-symbols.") + }, + 'breadcrumbs.filteredTypes.event': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.event', "When set to `false` breadcrumbs never show `event`-symbols.") + }, + 'breadcrumbs.filteredTypes.operator': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.operator', "When set to `false` breadcrumbs never show `operator`-symbols.") + }, + 'breadcrumbs.filteredTypes.typeParameter': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.typeParameter', "When set to `false` breadcrumbs never show `typeParameter`-symbols.") } } }); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 4594e365427..d102ba46008 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -17,7 +17,7 @@ import 'vs/css!./media/breadcrumbscontrol'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; -import { symbolKindToCssClass } from 'vs/editor/common/modes'; +import { SymbolKinds } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; @@ -109,7 +109,7 @@ class Item extends BreadcrumbsItem { // symbol if (this.options.showSymbolIcons) { let icon = document.createElement('div'); - icon.className = symbolKindToCssClass(this.element.symbol.kind); + icon.className = SymbolKinds.toCssClassName(this.element.symbol.kind); container.appendChild(icon); dom.addClass(container, 'shows-symbol-icon'); } @@ -130,15 +130,15 @@ export interface IBreadcrumbsControlOptions { export class BreadcrumbsControl { - static HEIGHT = 22; + static readonly HEIGHT = 22; static readonly Payload_Reveal = {}; static readonly Payload_RevealAside = {}; static readonly Payload_Pick = {}; - static CK_BreadcrumbsPossible = new RawContextKey('breadcrumbsPossible', false); - static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); - static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false); + static readonly CK_BreadcrumbsPossible = new RawContextKey('breadcrumbsPossible', false); + static readonly CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); + static readonly CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false); private readonly _ckBreadcrumbsPossible: IContextKey; private readonly _ckBreadcrumbsVisible: IContextKey; @@ -246,7 +246,7 @@ export class BreadcrumbsControl { const uri = input.getResource()!; const editor = this._getActiveCodeEditor(); - const model = new EditorBreadcrumbsModel(uri, editor, this._workspaceService, this._configurationService); + const model = new EditorBreadcrumbsModel(uri, editor, this._configurationService, this._workspaceService); dom.toggleClass(this.domNode, 'relative-path', model.isRelative()); dom.toggleClass(this.domNode, 'backslash-path', this._labelService.getSeparator(uri.scheme, uri.authority) === '\\'); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index c6f7164cc8e..44a223f32fc 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -14,7 +14,7 @@ import { isEqual, dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; -import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; +import { DocumentSymbolProviderRegistry, SymbolKinds } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { Schemas } from 'vs/base/common/network'; @@ -51,16 +51,15 @@ export class EditorBreadcrumbsModel { constructor( private readonly _uri: URI, private readonly _editor: ICodeEditor | undefined, + @IConfigurationService private readonly _configurationService: IConfigurationService, @IWorkspaceContextService workspaceService: IWorkspaceContextService, - @IConfigurationService configurationService: IConfigurationService, ) { - this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(configurationService); - this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(configurationService); + this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(_configurationService); + this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(_configurationService); this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this))); this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this))); - this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService); this._bindToEditor(); this._onDidUpdate.fire(this); @@ -138,6 +137,14 @@ export class EditorBreadcrumbsModel { this._disposables.add(this._editor.onDidChangeModel(_ => this._updateOutline())); this._disposables.add(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); + // update when config changes (re-render) + this._disposables.add(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('breadcrumbs.filteredTypes')) { + this._updateOutline(true); + } + })); + + // update soon'ish as model content change const updateSoon = new TimeoutTimer(); this._disposables.add(updateSoon); @@ -221,7 +228,18 @@ export class EditorBreadcrumbsModel { } item = parent; } - return chain.reverse(); + let result: Array = []; + for (let i = chain.length - 1; i >= 0; i--) { + let element = chain[i]; + if ( + element instanceof OutlineElement + && !this._configurationService.getValue(`breadcrumbs.filteredTypes.${SymbolKinds.toString(element.symbol.kind)}`) + ) { + break; + } + result.push(element); + } + return result; } private _updateOutlineElements(elements: Array): void { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index b34cd439d03..233bcba1b35 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -26,7 +26,7 @@ import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; -import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder } from 'vs/editor/contrib/documentSymbols/outlineTree'; +import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineFilter } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { @@ -450,7 +450,8 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { multipleSelectionSupport: false, sorter: new OutlineItemComparator(this._getOutlineItemCompareType()), identityProvider: new OutlineIdentityProvider(), - keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider() + keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(), + filter: this._instantiationService.createInstance(OutlineFilter, 'breadcrumbs.filteredTypes') } ); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 5f4afb20bc2..717579e2883 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -106,7 +106,7 @@ interface ISerializedUntitledEditorInput { resource: string; resourceJSON: object; modeId: string | undefined; - encoding: string; + encoding: string | undefined; } // Register Editor Input Factory diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 0aa5e53d0eb..167933b3db1 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -495,7 +495,7 @@ export class CloseOneEditorAction extends Action { group = this.editorGroupService.getGroup(context.groupId); if (group) { - editorIndex = context.editorIndex!; // only allow editor at index if group is valid + editorIndex = context.editorIndex; // only allow editor at index if group is valid } } diff --git a/src/vs/workbench/browser/parts/editor/editorControl.ts b/src/vs/workbench/browser/parts/editor/editorControl.ts index 898d7339fef..f96a342299a 100644 --- a/src/vs/workbench/browser/parts/editor/editorControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorControl.ts @@ -15,6 +15,7 @@ import { IEditorProgressService, LongRunningOperation } from 'vs/platform/progre import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; import { Event, Emitter } from 'vs/base/common/event'; import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService'; +import { assertIsDefined } from 'vs/base/common/types'; export interface IOpenEditorResult { readonly control: BaseEditor; @@ -88,8 +89,9 @@ export class EditorControl extends Disposable { this.doSetActiveControl(control); // Show editor - this.parent.appendChild(control.getContainer()); - show(control.getContainer()); + const container = assertIsDefined(control.getContainer()); + this.parent.appendChild(container); + show(container); // Indicate to editor that it is now visible control.setVisible(true, this.groupView); @@ -203,8 +205,10 @@ export class EditorControl extends Disposable { // Remove control from parent and hide const controlInstanceContainer = this._activeControl.getContainer(); - this.parent.removeChild(controlInstanceContainer); - hide(controlInstanceContainer); + if (controlInstanceContainer) { + this.parent.removeChild(controlInstanceContainer); + hide(controlInstanceContainer); + } // Indicate to editor control this._activeControl.clearInput(); diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 2113a4e7c50..b5089f88f4f 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -16,6 +16,7 @@ import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/editor/com import { toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { RunOnceScheduler } from 'vs/base/common/async'; +import { find } from 'vs/base/common/arrays'; interface IDropOperation { splitDirection?: GroupDirection; @@ -23,7 +24,7 @@ interface IDropOperation { class DropOverlay extends Themable { - private static OVERLAY_ID = 'monaco-workbench-editor-drop-overlay'; + private static readonly OVERLAY_ID = 'monaco-workbench-editor-drop-overlay'; private container!: HTMLElement; private overlay!: HTMLElement; @@ -84,7 +85,7 @@ class DropOverlay extends Themable { protected updateStyles(): void { // Overlay drop background - this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND); + this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) || ''; // Overlay contrast border (if any) const activeContrastBorderColor = this.getColor(activeContrastBorder); @@ -108,7 +109,16 @@ class DropOverlay extends Themable { } // Find out if operation is valid - const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier) : true; + let isCopy = true; + if (isDraggingGroup) { + isCopy = this.isCopyOperation(e); + } else if (isDraggingEditor) { + const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); + if (Array.isArray(data)) { + isCopy = this.isCopyOperation(e, data[0].identifier); + } + } + if (!isCopy) { const sourceGroupView = this.findSourceGroupView(); if (sourceGroupView === this.groupView) { @@ -162,12 +172,18 @@ class DropOverlay extends Themable { // Check for group transfer if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier); + const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype); + if (Array.isArray(data)) { + return this.accessor.getGroup(data[0].identifier); + } } // Check for editor transfer else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { - return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier.groupId); + const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); + if (Array.isArray(data)) { + return this.accessor.getGroup(data[0].identifier.groupId); + } } return undefined; @@ -189,69 +205,75 @@ class DropOverlay extends Themable { // Check for group transfer if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier; + const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype); + if (Array.isArray(data)) { + const draggedEditorGroup = data[0].identifier; - // Return if the drop is a no-op - const sourceGroup = this.accessor.getGroup(draggedEditorGroup); - if (sourceGroup) { - if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) { - return; - } + // Return if the drop is a no-op + const sourceGroup = this.accessor.getGroup(draggedEditorGroup); + if (sourceGroup) { + if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) { + return; + } - // Split to new group - let targetGroup: IEditorGroupView | undefined; - if (typeof splitDirection === 'number') { - if (this.isCopyOperation(event)) { - targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection); - } else { - targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection); + // Split to new group + let targetGroup: IEditorGroupView | undefined; + if (typeof splitDirection === 'number') { + if (this.isCopyOperation(event)) { + targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection); + } else { + targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection); + } + } + + // Merge into existing group + else { + if (this.isCopyOperation(event)) { + targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS }); + } else { + targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView); + } + } + + if (targetGroup) { + this.accessor.activateGroup(targetGroup); } } - // Merge into existing group - else { - if (this.isCopyOperation(event)) { - targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS }); - } else { - targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView); - } - } - - if (targetGroup) { - this.accessor.activateGroup(targetGroup); - } + this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype); } - - this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype); } // Check for editor transfer else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { - const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; - const targetGroup = ensureTargetGroup(); + const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); + if (Array.isArray(data)) { + const draggedEditor = data[0].identifier; + const targetGroup = ensureTargetGroup(); - // Return if the drop is a no-op - const sourceGroup = this.accessor.getGroup(draggedEditor.groupId); - if (sourceGroup) { - if (sourceGroup === targetGroup) { - return; + // Return if the drop is a no-op + const sourceGroup = this.accessor.getGroup(draggedEditor.groupId); + if (sourceGroup) { + if (sourceGroup === targetGroup) { + return; + } + + // Open in target group + const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true })); + targetGroup.openEditor(draggedEditor.editor, options); + + // Ensure target has focus + targetGroup.focus(); + + // Close in source group unless we copy + const copyEditor = this.isCopyOperation(event, draggedEditor); + if (!copyEditor) { + sourceGroup.closeEditor(draggedEditor.editor); + } } - // Open in target group - const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true })); - targetGroup.openEditor(draggedEditor.editor, options); - - // Ensure target has focus - targetGroup.focus(); - - // Close in source group unless we copy - const copyEditor = this.isCopyOperation(event, draggedEditor); - if (!copyEditor) { - sourceGroup.closeEditor(draggedEditor.editor); - } + this.editorTransfer.clearData(DraggedEditorIdentifier.prototype); } - - this.editorTransfer.clearData(DraggedEditorIdentifier.prototype); } // Check for URI transfer @@ -523,13 +545,7 @@ export class EditorDropTarget extends Themable { private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined { const groups = this.accessor.groups; - for (const groupView of groups) { - if (isAncestor(child, groupView.element)) { - return groupView; - } - } - - return undefined; + return find(groups, groupView => isAncestor(child, groupView.element)); } private updateContainer(isDraggedOver: boolean): void { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index c1de6319db0..3687fb2cba5 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -95,13 +95,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#endregion private _group: EditorGroup; - private _disposed: boolean; + private _disposed = false; - private active: boolean; - private dimension: Dimension; + private active: boolean | undefined; + private dimension: Dimension | undefined; private _whenRestored: Promise; - private isRestored: boolean; + private isRestored = false; private scopedInstantiationService: IInstantiationService; @@ -119,7 +119,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { constructor( private accessor: IEditorGroupsAccessor, - from: IEditorGroupView | ISerializedEditorGroup, + from: IEditorGroupView | ISerializedEditorGroup | null, private _index: number, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @@ -143,7 +143,68 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.disposedEditorsWorker = this._register(new RunOnceWorker(editors => this.handleDisposedEditors(editors), 0)); - this.create(); + //#region create() + { + // Container + addClasses(this.element, 'editor-group-container'); + + // Container listeners + this.registerContainerListeners(); + + // Container toolbar + this.createContainerToolbar(); + + // Container context menu + this.createContainerContextMenu(); + + // Letterpress container + const letterpressContainer = document.createElement('div'); + addClass(letterpressContainer, 'editor-group-letterpress'); + this.element.appendChild(letterpressContainer); + + // Progress bar + this.progressBar = this._register(new ProgressBar(this.element)); + this._register(attachProgressBarStyler(this.progressBar, this.themeService)); + this.progressBar.hide(); + + // Scoped services + const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.element)); + this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( + [IContextKeyService, scopedContextKeyService], + [IEditorProgressService, new EditorProgressService(this.progressBar)] + )); + + // Context keys + this.handleGroupContextKeys(scopedContextKeyService); + + // Title container + this.titleContainer = document.createElement('div'); + addClass(this.titleContainer, 'title'); + this.element.appendChild(this.titleContainer); + + // Title control + this.titleAreaControl = this.createTitleAreaControl(); + + // Editor container + this.editorContainer = document.createElement('div'); + addClass(this.editorContainer, 'editor-container'); + this.element.appendChild(this.editorContainer); + + // Editor control + this.editorControl = this._register(this.scopedInstantiationService.createInstance(EditorControl, this.editorContainer, this)); + this._onDidChange.input = this.editorControl.onDidSizeConstraintsChange; + + // Track Focus + this.doTrackFocus(); + + // Update containers + this.updateTitleContainer(); + this.updateContainer(); + + // Update styles + this.updateStyles(); + } + //#endregion this._whenRestored = this.restoreEditors(from); this._whenRestored.then(() => this.isRestored = true); @@ -151,68 +212,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.registerListeners(); } - private create(): void { - - // Container - addClasses(this.element, 'editor-group-container'); - - // Container listeners - this.registerContainerListeners(); - - // Container toolbar - this.createContainerToolbar(); - - // Container context menu - this.createContainerContextMenu(); - - // Letterpress container - const letterpressContainer = document.createElement('div'); - addClass(letterpressContainer, 'editor-group-letterpress'); - this.element.appendChild(letterpressContainer); - - // Progress bar - this.progressBar = this._register(new ProgressBar(this.element)); - this._register(attachProgressBarStyler(this.progressBar, this.themeService)); - this.progressBar.hide(); - - // Scoped services - const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.element)); - this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( - [IContextKeyService, scopedContextKeyService], - [IEditorProgressService, new EditorProgressService(this.progressBar)] - )); - - // Context keys - this.handleGroupContextKeys(scopedContextKeyService); - - // Title container - this.titleContainer = document.createElement('div'); - addClass(this.titleContainer, 'title'); - this.element.appendChild(this.titleContainer); - - // Title control - this.createTitleAreaControl(); - - // Editor container - this.editorContainer = document.createElement('div'); - addClass(this.editorContainer, 'editor-container'); - this.element.appendChild(this.editorContainer); - - // Editor control - this.editorControl = this._register(this.scopedInstantiationService.createInstance(EditorControl, this.editorContainer, this)); - this._onDidChange.input = this.editorControl.onDidSizeConstraintsChange; - - // Track Focus - this.doTrackFocus(); - - // Update containers - this.updateTitleContainer(); - this.updateContainer(); - - // Update styles - this.updateStyles(); - } - private handleGroupContextKeys(contextKeyService: IContextKeyService): void { const groupActiveEditorDirtyContextKey = EditorGroupActiveEditorDirtyContext.bindTo(contextKeyService); const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(contextKeyService); @@ -289,7 +288,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const removeGroupAction = this._register(new Action( CLOSE_EDITOR_GROUP_COMMAND_ID, localize('closeGroupAction', "Close"), - 'close-editor-group', + 'codicon-close', true, () => { this.accessor.removeGroup(this); @@ -404,7 +403,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { toggleClass(this.titleContainer, 'show-file-icons', this.accessor.partOptions.showIcons); } - private createTitleAreaControl(): void { + private createTitleAreaControl(): TitleControl { // Clear old if existing if (this.titleAreaControl) { @@ -418,9 +417,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } else { this.titleAreaControl = this.scopedInstantiationService.createInstance(NoTabsTitleControl, this.titleContainer, this.accessor, this); } + + return this.titleAreaControl; } - private async restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): Promise { + private async restoreEditors(from: IEditorGroupView | ISerializedEditorGroup | null): Promise { if (this._group.count === 0) { return; // nothing to show } @@ -596,7 +597,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Recreate and layout control this.createTitleAreaControl(); - this.layoutTitleAreaControl(); + if (this.dimension) { + this.layoutTitleAreaControl(this.dimension.width); + } // Ensure to show active editor if any if (this._group.activeEditor) { @@ -1442,9 +1445,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Container if (isEmpty) { - this.element.style.backgroundColor = this.getColor(EDITOR_GROUP_EMPTY_BACKGROUND); + this.element.style.backgroundColor = this.getColor(EDITOR_GROUP_EMPTY_BACKGROUND) || ''; } else { - this.element.style.backgroundColor = null; + this.element.style.backgroundColor = ''; } // Title control @@ -1459,10 +1462,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.titleContainer.style.removeProperty('--title-border-bottom-color'); } - this.titleContainer.style.backgroundColor = this.getColor(showTabs ? EDITOR_GROUP_HEADER_TABS_BACKGROUND : EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND); + this.titleContainer.style.backgroundColor = this.getColor(showTabs ? EDITOR_GROUP_HEADER_TABS_BACKGROUND : EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND) || ''; // Editor container - this.editorContainer.style.backgroundColor = this.getColor(editorBackground); + this.editorContainer.style.backgroundColor = this.getColor(editorBackground) || ''; } //#endregion @@ -1487,12 +1490,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.editorContainer.style.height = `calc(100% - ${this.titleAreaControl.getPreferredHeight()}px)`; // Forward to controls - this.layoutTitleAreaControl(); + this.layoutTitleAreaControl(width); this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - this.titleAreaControl.getPreferredHeight())); } - private layoutTitleAreaControl(): void { - this.titleAreaControl.layout(new Dimension(this.dimension.width, this.titleAreaControl.getPreferredHeight())); + private layoutTitleAreaControl(width: number): void { + this.titleAreaControl.layout(new Dimension(width, this.titleAreaControl.getPreferredHeight())); } relayout(): void { @@ -1520,7 +1523,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } class EditorOpeningEvent implements IEditorOpeningEvent { - private override: () => Promise; + private override: () => Promise | undefined; constructor( private _group: GroupIdentifier, @@ -1545,7 +1548,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent { this.override = callback; } - isPrevented(): () => Promise { + isPrevented(): () => Promise | undefined { return this.override; } } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index a3dec299bc6..9f5b31c4f55 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -796,7 +796,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro } updateStyles(): void { - this.container.style.backgroundColor = this.getColor(editorBackground); + this.container.style.backgroundColor = this.getColor(editorBackground) || ''; const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder, background: this.theme.getColor(EDITOR_PANE_BACKGROUND) || Color.transparent }; this.gridWidget.style(separatorBorderStyle); diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index a3d495473f5..1c8e7996d06 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -15,7 +15,7 @@ import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor'; +import { toResource, SideBySideEditor, IEditorInput } from 'vs/workbench/common/editor'; import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -23,7 +23,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; export class EditorPickerEntry extends QuickOpenEntryGroup { constructor( - private editor: EditorInput, + private editor: IEditorInput, private _group: IEditorGroup, @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 76ea1208dbf..2f2e982262e 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/editorstatus'; import * as nls from 'vs/nls'; import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { format } from 'vs/base/common/strings'; -import { extname, basename } from 'vs/base/common/resources'; +import { extname, basename, isEqual } from 'vs/base/common/resources'; import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; @@ -54,7 +54,7 @@ import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatus class SideBySideEditorEncodingSupport implements IEncodingSupport { constructor(private master: IEncodingSupport, private details: IEncodingSupport) { } - getEncoding(): string { + getEncoding(): string | undefined { return this.master.getEncoding(); // always report from modified (right hand) side } @@ -352,7 +352,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); } - const picks: QuickPickInput[] = [ + const picks: QuickPickInput[] = [ activeTextEditorWidget.getAction(IndentUsingSpaces.ID), activeTextEditorWidget.getAction(IndentUsingTabs.ID), activeTextEditorWidget.getAction(DetectIndentation.ID), @@ -730,20 +730,24 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { const textModel = editorWidget.getModel(); if (textModel) { info.selections.forEach(selection => { - info.charactersSelected! += textModel.getValueLengthInRange(selection); + if (typeof info.charactersSelected !== 'number') { + info.charactersSelected = 0; + } + + info.charactersSelected += textModel.getValueLengthInRange(selection); }); } // Compute the visible column for one selection. This will properly handle tabs and their configured widths if (info.selections.length === 1) { - const visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition()!); + const editorPosition = editorWidget.getPosition(); let selectionClone = info.selections[0].clone(); // do not modify the original position we got from the editor selectionClone = new Selection( selectionClone.selectionStartLineNumber, selectionClone.selectionStartColumn, selectionClone.positionLineNumber, - visibleColumn + editorPosition ? editorWidget.getVisibleColumnFromPosition(editorPosition) : selectionClone.positionColumn ); info.selections[0] = selectionClone; @@ -778,7 +782,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { const encodingSupport: IEncodingSupport | null = e.input ? toEditorWithEncodingSupport(e.input) : null; if (encodingSupport) { const rawEncoding = encodingSupport.getEncoding(); - const encodingInfo = SUPPORTED_ENCODINGS[rawEncoding]; + const encodingInfo = typeof rawEncoding === 'string' ? SUPPORTED_ENCODINGS[rawEncoding] : undefined; if (encodingInfo) { info.encoding = encodingInfo.labelShort; // if we have a label, take it from there } else { @@ -794,7 +798,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { const activeControl = this.editorService.activeControl; if (activeControl) { const activeResource = toResource(activeControl.input, { supportSideBySide: SideBySideEditor.MASTER }); - if (activeResource && activeResource.toString() === resource.toString()) { + if (activeResource && isEqual(activeResource, resource)) { return this.onEncodingChange(activeControl); // only update if the encoding changed for the active resource } } diff --git a/src/vs/workbench/browser/parts/editor/editorWidgets.ts b/src/vs/workbench/browser/parts/editor/editorWidgets.ts index 1588d1a9341..22ca587814e 100644 --- a/src/vs/workbench/browser/parts/editor/editorWidgets.ts +++ b/src/vs/workbench/browser/parts/editor/editorWidgets.ts @@ -8,7 +8,7 @@ import { IOverlayWidget, ICodeEditor, IOverlayWidgetPosition, OverlayWidgetPosit import { Event, Emitter } from 'vs/base/common/event'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { $, append } from 'vs/base/browser/dom'; +import { $, append, clearNode } from 'vs/base/browser/dom'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -31,12 +31,14 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { constructor( private editor: ICodeEditor, private label: string, - keyBindingAction: string, + keyBindingAction: string | null, @IKeybindingService keybindingService: IKeybindingService, @IThemeService private readonly themeService: IThemeService ) { super(); + this._domNode = $('.floating-click-widget'); + if (keyBindingAction) { const keybinding = keybindingService.lookupKeybinding(keyBindingAction); if (keybinding) { @@ -60,7 +62,7 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { } render() { - this._domNode = $('.floating-click-widget'); + clearNode(this._domNode); this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder }, colors => { const backgroundColor = colors.buttonBackground ? colors.buttonBackground : colors.editorBackground; @@ -73,9 +75,9 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { this._domNode.style.color = foregroundColor.toString(); } - const borderColor = colors.contrastBorder ? colors.contrastBorder.toString() : null; - this._domNode.style.borderWidth = borderColor ? '1px' : null; - this._domNode.style.borderStyle = borderColor ? 'solid' : null; + const borderColor = colors.contrastBorder ? colors.contrastBorder.toString() : ''; + this._domNode.style.borderWidth = borderColor ? '1px' : ''; + this._domNode.style.borderStyle = borderColor ? 'solid' : ''; this._domNode.style.borderColor = borderColor; })); diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 0c056ced27d..532c3378244 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -186,7 +186,7 @@ overflow: visible; /* ...but still show the close button on hover, focus and when dirty */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off:not(.dirty) > .tab-close { display: none; /* hide the close action bar when we are configured to hide it */ } @@ -233,10 +233,11 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top) { - background-repeat: no-repeat; - background-position-y: center; - background-position-x: calc(100% - 6px); /* to the right of the tab label */ - padding-right: 28px; /* make room for dirty indication when we are running without close button */ + padding-right: 0; /* remove extra padding when we are running without close button */ +} + +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close { + pointer-events: none; /* don't allow dirty state/close button to be clicked when running without close button */ } /* Editor Actions */ diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index ff9fde91e90..b170188ae15 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -14,7 +14,7 @@ import { EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { IAction } from 'vs/base/common/actions'; import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Color } from 'vs/base/common/color'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, assertIsDefined, assertAllDefined } from 'vs/base/common/types'; interface IRenderedEditorLabel { editor?: IEditorInput; @@ -22,23 +22,23 @@ interface IRenderedEditorLabel { } export class NoTabsTitleControl extends TitleControl { - private titleContainer: HTMLElement; - private editorLabel: IResourceLabel; + private titleContainer: HTMLElement | undefined; + private editorLabel: IResourceLabel | undefined; private activeLabel: IRenderedEditorLabel = Object.create(null); protected create(parent: HTMLElement): void { - this.titleContainer = parent; - this.titleContainer.draggable = true; + const titleContainer = this.titleContainer = parent; + titleContainer.draggable = true; //Container listeners - this.registerContainerListeners(); + this.registerContainerListeners(titleContainer); // Gesture Support - this._register(Gesture.addTarget(this.titleContainer)); + this._register(Gesture.addTarget(titleContainer)); const labelContainer = document.createElement('div'); addClass(labelContainer, 'label-container'); - this.titleContainer.appendChild(labelContainer); + titleContainer.appendChild(labelContainer); // Editor Label this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, undefined)).element; @@ -46,41 +46,41 @@ export class NoTabsTitleControl extends TitleControl { // Breadcrumbs this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: () => Color.transparent }); - toggleClass(this.titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl)); - this._register({ dispose: () => removeClass(this.titleContainer, 'breadcrumbs') }); // import to remove because the container is a shared dom node + toggleClass(titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl)); + this._register({ dispose: () => removeClass(titleContainer, 'breadcrumbs') }); // import to remove because the container is a shared dom node // Right Actions Container const actionsContainer = document.createElement('div'); addClass(actionsContainer, 'title-actions'); - this.titleContainer.appendChild(actionsContainer); + titleContainer.appendChild(actionsContainer); // Editor actions toolbar this.createEditorActionsToolBar(actionsContainer); } - private registerContainerListeners(): void { + private registerContainerListeners(titleContainer: HTMLElement): void { // Group dragging - this.enableGroupDragging(this.titleContainer); + this.enableGroupDragging(titleContainer); // Pin on double click - this._register(addDisposableListener(this.titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e))); + this._register(addDisposableListener(titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e))); // Detect mouse click - this._register(addDisposableListener(this.titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e))); + this._register(addDisposableListener(titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e))); // Detect touch - this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e))); + this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e))); // Context Menu - this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => { + this._register(addDisposableListener(titleContainer, EventType.CONTEXT_MENU, (e: Event) => { if (this.group.activeEditor) { - this.onContextMenu(this.group.activeEditor, e, this.titleContainer); + this.onContextMenu(this.group.activeEditor, e, titleContainer); } })); - this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => { + this._register(addDisposableListener(titleContainer, TouchEventType.Contextmenu, (e: Event) => { if (this.group.activeEditor) { - this.onContextMenu(this.group.activeEditor, e, this.titleContainer); + this.onContextMenu(this.group.activeEditor, e, titleContainer); } })); } @@ -157,10 +157,11 @@ export class NoTabsTitleControl extends TitleControl { updateEditorDirty(editor: IEditorInput): void { this.ifEditorIsActive(editor, () => { + const titleContainer = assertIsDefined(this.titleContainer); if (editor.isDirty()) { - addClass(this.titleContainer, 'dirty'); + addClass(titleContainer, 'dirty'); } else { - removeClass(this.titleContainer, 'dirty'); + removeClass(titleContainer, 'dirty'); } }); } @@ -176,7 +177,9 @@ export class NoTabsTitleControl extends TitleControl { } protected handleBreadcrumbsEnablementChange(): void { - toggleClass(this.titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl)); + const titleContainer = assertIsDefined(this.titleContainer); + + toggleClass(titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl)); this.redraw(); } @@ -230,9 +233,10 @@ export class NoTabsTitleControl extends TitleControl { } // Clear if there is no editor + const [titleContainer, editorLabel] = assertAllDefined(this.titleContainer, this.editorLabel); if (!editor) { - removeClass(this.titleContainer, 'dirty'); - this.editorLabel.clear(); + removeClass(titleContainer, 'dirty'); + editorLabel.clear(); this.clearEditorActionsToolbar(); } @@ -261,11 +265,11 @@ export class NoTabsTitleControl extends TitleControl { title = ''; // dont repeat what is already shown } - this.editorLabel.setResource({ name, description, resource: resource || undefined }, { title: typeof title === 'string' ? title : undefined, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] }); + editorLabel.setResource({ name, description, resource: resource || undefined }, { title: typeof title === 'string' ? title : undefined, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] }); if (isGroupActive) { - this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND); + editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND); } else { - this.editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND); + editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND); } // Update Editor Actions Toolbar diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index 773755ec961..c87b88479de 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -16,7 +16,7 @@ import { IMAGE_PREVIEW_BORDER } from 'vs/workbench/common/theme'; export interface IResourceDescriptor { readonly resource: URI; readonly name: string; - readonly size: number; + readonly size?: number; readonly etag?: string; readonly mime: string; } @@ -82,8 +82,8 @@ export class ResourceViewer { container.className = 'monaco-resource-viewer'; // Large Files - if (descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) { - return FileTooLargeFileView.create(container, descriptor, scrollbar, delegate); + if (typeof descriptor.size === 'number' && descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) { + return FileTooLargeFileView.create(container, descriptor.size, scrollbar, delegate); } // Seemingly Binary Files @@ -96,11 +96,11 @@ export class ResourceViewer { class FileTooLargeFileView { static create( container: HTMLElement, - descriptor: IResourceDescriptor, + descriptorSize: number, scrollbar: DomScrollableElement, delegate: ResourceViewerDelegate ) { - const size = BinarySize.formatSize(descriptor.size); + const size = BinarySize.formatSize(descriptorSize); delegate.metadataClb(size); DOM.clearNode(container); diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index b05cb17d70f..9d8ae67d35b 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -17,6 +17,7 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { Event, Relay, Emitter } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { assertIsDefined } from 'vs/base/common/types'; export class SideBySideEditor extends BaseEditor { @@ -47,10 +48,10 @@ export class SideBySideEditor extends BaseEditor { protected masterEditor?: BaseEditor; protected detailsEditor?: BaseEditor; - private masterEditorContainer: HTMLElement; - private detailsEditorContainer: HTMLElement; + private masterEditorContainer: HTMLElement | undefined; + private detailsEditorContainer: HTMLElement | undefined; - private splitview: SplitView; + private splitview: SplitView | undefined; private dimension: DOM.Dimension = new DOM.Dimension(0, 0); private onDidCreateEditors = this._register(new Emitter<{ width: number; height: number; } | undefined>()); @@ -69,8 +70,8 @@ export class SideBySideEditor extends BaseEditor { protected createEditor(parent: HTMLElement): void { DOM.addClass(parent, 'side-by-side-editor'); - this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL })); - this._register(this.splitview.onDidSashReset(() => this.splitview.distributeViewSizes())); + const splitview = this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL })); + this._register(this.splitview.onDidSashReset(() => splitview.distributeViewSizes())); this.detailsEditorContainer = DOM.$('.details-editor-container'); this.splitview.addView({ @@ -140,7 +141,9 @@ export class SideBySideEditor extends BaseEditor { layout(dimension: DOM.Dimension): void { this.dimension = dimension; - this.splitview.layout(dimension.width); + + const splitview = assertIsDefined(this.splitview); + splitview.layout(dimension.width); } getControl(): IEditorControl | undefined { @@ -179,8 +182,8 @@ export class SideBySideEditor extends BaseEditor { } private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - const detailsEditor = this.doCreateEditor(newInput.details, this.detailsEditorContainer); - const masterEditor = this.doCreateEditor(newInput.master, this.masterEditorContainer); + const detailsEditor = this.doCreateEditor(newInput.details, assertIsDefined(this.detailsEditorContainer)); + const masterEditor = this.doCreateEditor(newInput.master, assertIsDefined(this.masterEditorContainer)); return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token); } @@ -234,8 +237,13 @@ export class SideBySideEditor extends BaseEditor { this.masterEditor = undefined; } - this.detailsEditorContainer.innerHTML = ''; - this.masterEditorContainer.innerHTML = ''; + if (this.detailsEditorContainer) { + DOM.clearNode(this.detailsEditorContainer); + } + + if (this.masterEditorContainer) { + DOM.clearNode(this.masterEditorContainer); + } } dispose(): void { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 691ae0570ac..9ff0ac17f9c 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -40,11 +40,11 @@ import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorAc import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; import { IFileService } from 'vs/platform/files/common/files'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, assertAllDefined, assertIsDefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; interface IEditorInputLabel { - name: string; + name?: string; description?: string; title?: string; } @@ -53,19 +53,20 @@ type AugmentedLabel = IEditorInputLabel & { editor: IEditorInput }; export class TabsTitleControl extends TitleControl { - private titleContainer: HTMLElement; - private tabsContainer: HTMLElement; - private editorToolbarContainer: HTMLElement; - private tabsScrollbar: ScrollableElement; + private titleContainer: HTMLElement | undefined; + private tabsContainer: HTMLElement | undefined; + private editorToolbarContainer: HTMLElement | undefined; + private tabsScrollbar: ScrollableElement | undefined; + private closeOneEditorAction: CloseOneEditorAction; private tabResourceLabels: ResourceLabels; private tabLabels: IEditorInputLabel[] = []; private tabDisposables: IDisposable[] = []; - private dimension: Dimension; + private dimension: Dimension | undefined; private readonly layoutScheduled = this._register(new MutableDisposable()); - private blockRevealActiveTab: boolean; + private blockRevealActiveTab: boolean | undefined; constructor( parent: HTMLElement, @@ -87,6 +88,9 @@ export class TabsTitleControl extends TitleControl { @ILabelService labelService: ILabelService ) { super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService, labelService); + + this.tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER)); + this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL)); } protected create(parent: HTMLElement): void { @@ -103,13 +107,13 @@ export class TabsTitleControl extends TitleControl { this.tabsContainer.draggable = true; addClass(this.tabsContainer, 'tabs-container'); - // Tabs Container listeners - this.registerTabsContainerListeners(); - // Tabs Scrollbar this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer)); tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode()); + // Tabs Container listeners + this.registerTabsContainerListeners(this.tabsContainer, this.tabsScrollbar); + // Editor Toolbar Container this.editorToolbarContainer = document.createElement('div'); addClass(this.editorToolbarContainer, 'editor-actions'); @@ -118,17 +122,11 @@ export class TabsTitleControl extends TitleControl { // Editor Actions Toolbar this.createEditorActionsToolBar(this.editorToolbarContainer); - // Close Action - this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL)); - // Breadcrumbs (are on a separate row below tabs and actions) const breadcrumbsContainer = document.createElement('div'); addClass(breadcrumbsContainer, 'tabs-breadcrumbs'); this.titleContainer.appendChild(breadcrumbsContainer); this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: breadcrumbsBackground }); - - // Tab Labels - this.tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER)); } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { @@ -160,23 +158,23 @@ export class TabsTitleControl extends TitleControl { this.group.relayout(); } - private registerTabsContainerListeners(): void { + private registerTabsContainerListeners(tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): void { // Group dragging - this.enableGroupDragging(this.tabsContainer); + this.enableGroupDragging(tabsContainer); // Forward scrolling inside the container to our custom scrollbar - this._register(addDisposableListener(this.tabsContainer, EventType.SCROLL, () => { - if (hasClass(this.tabsContainer, 'scroll')) { - this.tabsScrollbar.setScrollPosition({ - scrollLeft: this.tabsContainer.scrollLeft // during DND the container gets scrolled so we need to update the custom scrollbar + this._register(addDisposableListener(tabsContainer, EventType.SCROLL, () => { + if (hasClass(tabsContainer, 'scroll')) { + tabsScrollbar.setScrollPosition({ + scrollLeft: tabsContainer.scrollLeft // during DND the container gets scrolled so we need to update the custom scrollbar }); } })); // New file when double clicking on tabs container (but not tabs) - this._register(addDisposableListener(this.tabsContainer, EventType.DBLCLICK, e => { - if (e.target === this.tabsContainer) { + this._register(addDisposableListener(tabsContainer, EventType.DBLCLICK, e => { + if (e.target === tabsContainer) { EventHelper.stop(e); this.group.openEditor(this.untitledEditorService.createOrGet(), { pinned: true /* untitled is always pinned */, index: this.group.count /* always at the end */ }); @@ -184,29 +182,31 @@ export class TabsTitleControl extends TitleControl { })); // Prevent auto-scrolling (https://github.com/Microsoft/vscode/issues/16690) - this._register(addDisposableListener(this.tabsContainer, EventType.MOUSE_DOWN, (e: MouseEvent) => { + this._register(addDisposableListener(tabsContainer, EventType.MOUSE_DOWN, (e: MouseEvent) => { if (e.button === 1) { e.preventDefault(); } })); - // Drop support - this._register(new DragAndDropObserver(this.tabsContainer, { + this._register(new DragAndDropObserver(tabsContainer, { onDragEnter: e => { // Always enable support to scroll while dragging - addClass(this.tabsContainer, 'scroll'); + addClass(tabsContainer, 'scroll'); // Return if the target is not on the tabs container - if (e.target !== this.tabsContainer) { - this.updateDropFeedback(this.tabsContainer, false); // fixes https://github.com/Microsoft/vscode/issues/52093 + if (e.target !== tabsContainer) { + this.updateDropFeedback(tabsContainer, false); // fixes https://github.com/Microsoft/vscode/issues/52093 return; } // Return if transfer is unsupported if (!this.isSupportedDropTransfer(e)) { - e.dataTransfer!.dropEffect = 'none'; + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'none'; + } + return; } @@ -215,38 +215,46 @@ export class TabsTitleControl extends TitleControl { if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { isLocalDragAndDrop = true; - const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; - if (this.group.id === localDraggedEditor.groupId && this.group.getIndexOfEditor(localDraggedEditor.editor) === this.group.count - 1) { - e.dataTransfer!.dropEffect = 'none'; - return; + const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); + if (Array.isArray(data)) { + const localDraggedEditor = data[0].identifier; + if (this.group.id === localDraggedEditor.groupId && this.group.getIndexOfEditor(localDraggedEditor.editor) === this.group.count - 1) { + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'none'; + } + + return; + } } } // Update the dropEffect to "copy" if there is no local data to be dragged because // in that case we can only copy the data into and not move it from its source if (!isLocalDragAndDrop) { - e.dataTransfer!.dropEffect = 'copy'; + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'copy'; + } } - this.updateDropFeedback(this.tabsContainer, true); + this.updateDropFeedback(tabsContainer, true); }, onDragLeave: e => { - this.updateDropFeedback(this.tabsContainer, false); - removeClass(this.tabsContainer, 'scroll'); + this.updateDropFeedback(tabsContainer, false); + removeClass(tabsContainer, 'scroll'); }, onDragEnd: e => { - this.updateDropFeedback(this.tabsContainer, false); - removeClass(this.tabsContainer, 'scroll'); + this.updateDropFeedback(tabsContainer, false); + removeClass(tabsContainer, 'scroll'); }, onDrop: e => { - this.updateDropFeedback(this.tabsContainer, false); - removeClass(this.tabsContainer, 'scroll'); + this.updateDropFeedback(tabsContainer, false); + removeClass(tabsContainer, 'scroll'); - if (e.target === this.tabsContainer) { - this.onDrop(e, this.group.count); + if (e.target === tabsContainer) { + this.onDrop(e, this.group.count, tabsContainer); } } })); @@ -263,8 +271,9 @@ export class TabsTitleControl extends TitleControl { openEditor(editor: IEditorInput): void { // Create tabs as needed - for (let i = this.tabsContainer.children.length; i < this.group.count; i++) { - this.tabsContainer.appendChild(this.createTab(i)); + const [tabsContainer, tabsScrollbar] = assertAllDefined(this.tabsContainer, this.tabsScrollbar); + for (let i = tabsContainer.children.length; i < this.group.count; i++) { + tabsContainer.appendChild(this.createTab(i, tabsContainer, tabsScrollbar)); } // An add of a tab requires to recompute all labels @@ -295,10 +304,11 @@ export class TabsTitleControl extends TitleControl { if (this.group.activeEditor) { // Remove tabs that got closed - while (this.tabsContainer.children.length > this.group.count) { + const tabsContainer = assertIsDefined(this.tabsContainer); + while (tabsContainer.children.length > this.group.count) { // Remove one tab from container (must be the last to keep indexes in order!) - (this.tabsContainer.lastChild as HTMLElement).remove(); + (tabsContainer.lastChild as HTMLElement).remove(); // Remove associated tab label and widget this.tabDisposables.pop()!.dispose(); @@ -313,7 +323,9 @@ export class TabsTitleControl extends TitleControl { // No tabs to show else { - clearNode(this.tabsContainer); + if (this.tabsContainer) { + clearNode(this.tabsContainer); + } this.tabDisposables = dispose(this.tabDisposables); this.tabResourceLabels.clear(); @@ -409,13 +421,14 @@ export class TabsTitleControl extends TitleControl { private withTab(editor: IEditorInput, fn: (tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { const editorIndex = this.group.getIndexOfEditor(editor); - const tabContainer = this.tabsContainer.children[editorIndex] as HTMLElement; + const tabsContainer = assertIsDefined(this.tabsContainer); + const tabContainer = tabsContainer.children[editorIndex] as HTMLElement; if (tabContainer) { fn(tabContainer, this.tabResourceLabels.get(editorIndex), this.tabLabels[editorIndex]); } } - private createTab(index: number): HTMLElement { + private createTab(index: number, tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): HTMLElement { // Tab Container const tabContainer = document.createElement('div'); @@ -452,14 +465,14 @@ export class TabsTitleControl extends TitleControl { tabActionBar.onDidBeforeRun(() => this.blockRevealActiveTabOnce()); // Eventing - const eventsDisposable = this.registerTabListeners(tabContainer, index); + const eventsDisposable = this.registerTabListeners(tabContainer, index, tabsContainer, tabsScrollbar); this.tabDisposables.push(combinedDisposable(eventsDisposable, tabActionBar, tabActionRunner, editorLabel)); return tabContainer; } - private registerTabListeners(tab: HTMLElement, index: number): IDisposable { + private registerTabListeners(tab: HTMLElement, index: number, tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): IDisposable { const disposables = new DisposableStore(); const handleClickOrTouch = (e: MouseEvent | GestureEvent): void => { @@ -501,7 +514,7 @@ export class TabsTitleControl extends TitleControl { // Touch Scroll Support disposables.add(addDisposableListener(tab, TouchEventType.Change, (e: GestureEvent) => { - this.tabsScrollbar.setScrollPosition({ scrollLeft: this.tabsScrollbar.getScrollPosition().scrollLeft - e.translationX }); + tabsScrollbar.setScrollPosition({ scrollLeft: tabsScrollbar.getScrollPosition().scrollLeft - e.translationX }); })); // Close on mouse middle click @@ -562,7 +575,7 @@ export class TabsTitleControl extends TitleControl { if (target) { handled = true; this.group.openEditor(target, { preserveFocus: true }); - (this.tabsContainer.childNodes[targetIndex]).focus(); + (tabsContainer.childNodes[targetIndex]).focus(); } } @@ -571,8 +584,8 @@ export class TabsTitleControl extends TitleControl { } // moving in the tabs container can have an impact on scrolling position, so we need to update the custom scrollbar - this.tabsScrollbar.setScrollPosition({ - scrollLeft: this.tabsContainer.scrollLeft + tabsScrollbar.setScrollPosition({ + scrollLeft: tabsContainer.scrollLeft }); })); @@ -602,7 +615,9 @@ export class TabsTitleControl extends TitleControl { this.editorTransfer.setData([new DraggedEditorIdentifier({ editor, groupId: this.group.id })], DraggedEditorIdentifier.prototype); - e.dataTransfer!.effectAllowed = 'copyMove'; + if (e.dataTransfer) { + e.dataTransfer.effectAllowed = 'copyMove'; + } // Apply some datatransfer types to allow for dragging the element outside of the application const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); @@ -624,7 +639,10 @@ export class TabsTitleControl extends TitleControl { // Return if transfer is unsupported if (!this.isSupportedDropTransfer(e)) { - e.dataTransfer!.dropEffect = 'none'; + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'none'; + } + return; } @@ -633,17 +651,25 @@ export class TabsTitleControl extends TitleControl { if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { isLocalDragAndDrop = true; - const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; - if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) { - e.dataTransfer!.dropEffect = 'none'; - return; + const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); + if (Array.isArray(data)) { + const localDraggedEditor = data[0].identifier; + if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) { + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'none'; + } + + return; + } } } // Update the dropEffect to "copy" if there is no local data to be dragged because // in that case we can only copy the data into and not move it from its source if (!isLocalDragAndDrop) { - e.dataTransfer!.dropEffect = 'copy'; + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'copy'; + } } this.updateDropFeedback(tab, true, index); @@ -665,7 +691,7 @@ export class TabsTitleControl extends TitleControl { removeClass(tab, 'dragged-over'); this.updateDropFeedback(tab, false, index); - this.onDrop(e, index); + this.onDrop(e, index, tabsContainer); } })); @@ -674,9 +700,12 @@ export class TabsTitleControl extends TitleControl { private isSupportedDropTransfer(e: DragEvent): boolean { if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0]; - if (group.identifier === this.group.id) { - return false; // groups cannot be dropped on title area it originates from + const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype); + if (Array.isArray(data)) { + const group = data[0]; + if (group.identifier === this.group.id) { + return false; // groups cannot be dropped on title area it originates from + } } return true; @@ -699,8 +728,8 @@ export class TabsTitleControl extends TitleControl { const isActiveTab = isTab && !!editor && this.group.isActive(editor); // Background - const noDNDBackgroundColor = isTab ? this.getColor(isActiveTab ? TAB_ACTIVE_BACKGROUND : TAB_INACTIVE_BACKGROUND) : null; - element.style.backgroundColor = isDND ? this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) : noDNDBackgroundColor; + const noDNDBackgroundColor = isTab ? this.getColor(isActiveTab ? TAB_ACTIVE_BACKGROUND : TAB_INACTIVE_BACKGROUND) : ''; + element.style.backgroundColor = (isDND ? this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) : noDNDBackgroundColor) || ''; // Outline const activeContrastBorderColor = this.getColor(activeContrastBorder); @@ -724,7 +753,7 @@ export class TabsTitleControl extends TitleControl { // Build labels and descriptions for each editor const labels = this.group.editors.map(editor => ({ editor, - name: editor.getName()!, + name: editor.getName(), description: editor.getDescription(verbosity), title: withNullAsUndefined(editor.getTitle(Verbosity.LONG)) })); @@ -835,7 +864,8 @@ export class TabsTitleControl extends TitleControl { private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { this.group.editors.forEach((editor, index) => { - const tabContainer = this.tabsContainer.children[index] as HTMLElement; + const tabsContainer = assertIsDefined(this.tabsContainer); + const tabContainer = tabsContainer.children[index] as HTMLElement; if (tabContainer) { fn(editor, index, tabContainer, this.tabResourceLabels.get(index), this.tabLabels[index]); } @@ -849,7 +879,7 @@ export class TabsTitleControl extends TitleControl { // Borders / Outline const borderRightColor = (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)); - tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : null; + tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : ''; tabContainer.style.outlineColor = this.getColor(activeContrastBorder) || ''; // Settings @@ -904,7 +934,7 @@ export class TabsTitleControl extends TitleControl { // Container addClass(tabContainer, 'active'); tabContainer.setAttribute('aria-selected', 'true'); - tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_ACTIVE_BACKGROUND : TAB_UNFOCUSED_ACTIVE_BACKGROUND); + tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_ACTIVE_BACKGROUND : TAB_UNFOCUSED_ACTIVE_BACKGROUND) || ''; const activeTabBorderColorBottom = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER : TAB_UNFOCUSED_ACTIVE_BORDER); if (activeTabBorderColorBottom) { @@ -934,8 +964,8 @@ export class TabsTitleControl extends TitleControl { // Container removeClass(tabContainer, 'active'); tabContainer.setAttribute('aria-selected', 'false'); - tabContainer.style.backgroundColor = this.getColor(TAB_INACTIVE_BACKGROUND); - tabContainer.style.boxShadow = null; + tabContainer.style.backgroundColor = this.getColor(TAB_INACTIVE_BACKGROUND) || ''; + tabContainer.style.boxShadow = ''; // Label tabLabelWidget.element.style.color = this.getColor(isGroupActive ? TAB_INACTIVE_FOREGROUND : TAB_UNFOCUSED_INACTIVE_FOREGROUND); @@ -985,7 +1015,7 @@ export class TabsTitleControl extends TitleControl { return hasModifiedBorderColor; } - layout(dimension: Dimension): void { + layout(dimension: Dimension | undefined): void { this.dimension = dimension; const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined; @@ -998,7 +1028,9 @@ export class TabsTitleControl extends TitleControl { // this a little bit we try at least to schedule this work on the next animation frame. if (!this.layoutScheduled.value) { this.layoutScheduled.value = scheduleAtNextAnimationFrame(() => { - this.doLayout(this.dimension); + const dimension = assertIsDefined(this.dimension); + this.doLayout(dimension); + this.layoutScheduled.clear(); }); } @@ -1010,16 +1042,18 @@ export class TabsTitleControl extends TitleControl { return; } + const [tabsContainer, tabsScrollbar] = assertAllDefined(this.tabsContainer, this.tabsScrollbar); + if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) { this.breadcrumbsControl.layout({ width: dimension.width, height: BreadcrumbsControl.HEIGHT }); - this.tabsScrollbar.getDomNode().style.height = `${dimension.height - BreadcrumbsControl.HEIGHT}px`; + tabsScrollbar.getDomNode().style.height = `${dimension.height - BreadcrumbsControl.HEIGHT}px`; } - const visibleContainerWidth = this.tabsContainer.offsetWidth; - const totalContainerWidth = this.tabsContainer.scrollWidth; + const visibleContainerWidth = tabsContainer.offsetWidth; + const totalContainerWidth = tabsContainer.scrollWidth; - let activeTabPosX: number; - let activeTabWidth: number; + let activeTabPosX: number | undefined; + let activeTabWidth: number | undefined; if (!this.blockRevealActiveTab) { activeTabPosX = activeTab.offsetLeft; @@ -1027,33 +1061,33 @@ export class TabsTitleControl extends TitleControl { } // Update scrollbar - this.tabsScrollbar.setScrollDimensions({ + tabsScrollbar.setScrollDimensions({ width: visibleContainerWidth, scrollWidth: totalContainerWidth }); // Return now if we are blocked to reveal the active tab and clear flag - if (this.blockRevealActiveTab) { + if (this.blockRevealActiveTab || typeof activeTabPosX !== 'number' || typeof activeTabWidth !== 'number') { this.blockRevealActiveTab = false; return; } // Reveal the active one - const containerScrollPosX = this.tabsScrollbar.getScrollPosition().scrollLeft; - const activeTabFits = activeTabWidth! <= visibleContainerWidth; + const containerScrollPosX = tabsScrollbar.getScrollPosition().scrollLeft; + const activeTabFits = activeTabWidth <= visibleContainerWidth; // Tab is overflowing to the right: Scroll minimally until the element is fully visible to the right // Note: only try to do this if we actually have enough width to give to show the tab fully! - if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX! + activeTabWidth!) { - this.tabsScrollbar.setScrollPosition({ - scrollLeft: containerScrollPosX + ((activeTabPosX! + activeTabWidth!) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */) + if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX + activeTabWidth) { + tabsScrollbar.setScrollPosition({ + scrollLeft: containerScrollPosX + ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */) }); } // Tab is overlflowng to the left or does not fit: Scroll it into view to the left - else if (containerScrollPosX > activeTabPosX! || !activeTabFits) { - this.tabsScrollbar.setScrollPosition({ - scrollLeft: activeTabPosX! + else if (containerScrollPosX > activeTabPosX || !activeTabFits) { + tabsScrollbar.setScrollPosition({ + scrollLeft: activeTabPosX }); } } @@ -1061,7 +1095,9 @@ export class TabsTitleControl extends TitleControl { private getTab(editor: IEditorInput): HTMLElement | undefined { const editorIndex = this.group.getIndexOfEditor(editor); if (editorIndex >= 0) { - return this.tabsContainer.children[editorIndex] as HTMLElement; + const tabsContainer = assertIsDefined(this.tabsContainer); + + return tabsContainer.children[editorIndex] as HTMLElement; } return undefined; @@ -1088,49 +1124,55 @@ export class TabsTitleControl extends TitleControl { return !!findParentWithClass(element, 'action-item', 'tab'); } - private onDrop(e: DragEvent, targetIndex: number): void { + private onDrop(e: DragEvent, targetIndex: number, tabsContainer: HTMLElement): void { EventHelper.stop(e, true); - this.updateDropFeedback(this.tabsContainer, false); - removeClass(this.tabsContainer, 'scroll'); + this.updateDropFeedback(tabsContainer, false); + removeClass(tabsContainer, 'scroll'); // Local Editor DND if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { - const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; - const sourceGroup = this.accessor.getGroup(draggedEditor.groupId); + const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); + if (Array.isArray(data)) { + const draggedEditor = data[0].identifier; + const sourceGroup = this.accessor.getGroup(draggedEditor.groupId); - if (sourceGroup) { + if (sourceGroup) { - // Move editor to target position and index - if (this.isMoveOperation(e, draggedEditor.groupId)) { - sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex }); + // Move editor to target position and index + if (this.isMoveOperation(e, draggedEditor.groupId)) { + sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex }); + } + + // Copy editor to target position and index + else { + sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex }); + } } - // Copy editor to target position and index - else { - sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex }); - } + this.group.focus(); + this.editorTransfer.clearData(DraggedEditorIdentifier.prototype); } - - this.group.focus(); - this.editorTransfer.clearData(DraggedEditorIdentifier.prototype); } // Local Editor Group DND else if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier); + const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype); + if (data) { + const sourceGroup = this.accessor.getGroup(data[0].identifier); - if (sourceGroup) { - const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex }; - if (!this.isMoveOperation(e, sourceGroup.id)) { - mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS; + if (sourceGroup) { + const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex }; + if (!this.isMoveOperation(e, sourceGroup.id)) { + mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS; + } + + this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions); } - this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions); + this.group.focus(); + this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype); } - - this.group.focus(); - this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype); } // External DND diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index cec875895b5..e4d132cc598 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import * as objects from 'vs/base/common/objects'; -import { isFunction, isObject, isArray } from 'vs/base/common/types'; +import { isFunction, isObject, isArray, assertIsDefined } from 'vs/base/common/types'; import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; @@ -100,7 +100,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { } // Set Editor Model - const diffEditor = this.getControl(); + const diffEditor = assertIsDefined(this.getControl()); const resolvedDiffEditorModel = resolvedModel; diffEditor.setModel(resolvedDiffEditorModel.textDiffEditorModel); @@ -113,7 +113,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { // Otherwise restore View State let hasPreviousViewState = false; if (!optionsGotApplied) { - hasPreviousViewState = this.restoreTextDiffEditorViewState(input); + hasPreviousViewState = this.restoreTextDiffEditorViewState(input, diffEditor); } // Diff navigator @@ -138,17 +138,18 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { setOptions(options: EditorOptions | undefined): void { const textOptions = options; if (textOptions && isFunction(textOptions.apply)) { - textOptions.apply(this.getControl(), ScrollType.Smooth); + const diffEditor = assertIsDefined(this.getControl()); + textOptions.apply(diffEditor, ScrollType.Smooth); } } - private restoreTextDiffEditorViewState(input: EditorInput): boolean { - if (input instanceof DiffEditorInput) { - const resource = this.toDiffEditorViewStateResource(input); + private restoreTextDiffEditorViewState(editor: EditorInput, control: IDiffEditor): boolean { + if (editor instanceof DiffEditorInput) { + const resource = this.toDiffEditorViewStateResource(editor); if (resource) { const viewState = this.loadTextEditorViewState(resource); if (viewState) { - this.getControl().restoreViewState(viewState); + control.restoreViewState(viewState); return true; } @@ -258,7 +259,10 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { this.saveTextDiffEditorViewState(this.input); // Clear Model - this.getControl().setModel(null); + const diffEditor = this.getControl(); + if (diffEditor) { + diffEditor.setModel(null); + } // Pass to super super.clearInput(); @@ -268,8 +272,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { return this.diffNavigator; } - getControl(): IDiffEditor { - return super.getControl() as IDiffEditor; + getControl(): IDiffEditor | undefined { + return super.getControl() as IDiffEditor | undefined; } protected loadTextEditorViewState(resource: URI): IDiffEditorViewState { @@ -307,7 +311,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { } private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState | null { - const control = this.getControl(); + const control = assertIsDefined(this.getControl()); const model = control.getModel(); if (!model || !model.modified || !model.original) { return null; // view state always needs a model diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index fd36123986e..ab5403342c3 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import * as objects from 'vs/base/common/objects'; -import * as types from 'vs/base/common/types'; -import * as DOM from 'vs/base/browser/dom'; +import { distinct, deepClone, assign } from 'vs/base/common/objects'; +import { isObject, assertIsDefined } from 'vs/base/common/types'; +import { Dimension } from 'vs/base/browser/dom'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorInput, EditorOptions, IEditorMemento, ITextEditor } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; @@ -37,8 +37,8 @@ export interface IEditorConfiguration { * be subclassed and not instantiated. */ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { - private editorControl: IEditor; - private _editorContainer: HTMLElement; + private editorControl: IEditor | undefined; + private _editorContainer: HTMLElement | undefined; private hasPendingConfigurationChange: boolean | undefined; private lastAppliedEditorOptions?: IEditorOptions; private editorMemento: IEditorMemento; @@ -96,8 +96,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { protected computeConfiguration(configuration: IEditorConfiguration): IEditorOptions { // Specific editor options always overwrite user configuration - const editorConfiguration: IEditorOptions = types.isObject(configuration.editor) ? objects.deepClone(configuration.editor) : Object.create(null); - objects.assign(editorConfiguration, this.getConfigurationOverrides()); + const editorConfiguration: IEditorOptions = isObject(configuration.editor) ? deepClone(configuration.editor) : Object.create(null); + assign(editorConfiguration, this.getConfigurationOverrides()); // ARIA label editorConfiguration.ariaLabel = this.computeAriaLabel(); @@ -111,7 +111,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { // Apply group information to help identify in which group we are if (ariaLabel) { if (this.group) { - ariaLabel = nls.localize('editorLabelWithGroup', "{0}, {1}.", ariaLabel, this.group.label); + ariaLabel = localize('editorLabelWithGroup', "{0}, {1}.", ariaLabel, this.group.label); } } @@ -120,7 +120,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { protected getConfigurationOverrides(): IEditorOptions { const overrides = {}; - objects.assign(overrides, { + assign(overrides, { overviewRulerLanes: 3, lineNumbersMinChars: 3, fixedOverflowWidgets: true @@ -133,7 +133,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { // Editor for Text this._editorContainer = parent; - this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue(this.getResource()!)))); + this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue(this.getResource())))); // Model & Language changes const codeEditor = getCodeEditor(this.editorControl); @@ -197,33 +197,40 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { // Update editor options after having set the input. We do this because there can be // editor input specific options (e.g. an ARIA label depending on the input showing) this.updateEditorConfiguration(); - this._editorContainer.setAttribute('aria-label', this.computeAriaLabel()); + + const editorContainer = assertIsDefined(this._editorContainer); + editorContainer.setAttribute('aria-label', this.computeAriaLabel()); } protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { // Pass on to Editor + const editorControl = assertIsDefined(this.editorControl); if (visible) { this.consumePendingConfigurationChangeEvent(); - this.editorControl.onVisible(); + editorControl.onVisible(); } else { - this.editorControl.onHide(); + editorControl.onHide(); } super.setEditorVisible(visible, group); } focus(): void { - this.editorControl.focus(); - } - - layout(dimension: DOM.Dimension): void { // Pass on to Editor - this.editorControl.layout(dimension); + const editorControl = assertIsDefined(this.editorControl); + editorControl.focus(); } - getControl(): IEditor { + layout(dimension: Dimension): void { + + // Pass on to Editor + const editorControl = assertIsDefined(this.editorControl); + editorControl.layout(dimension); + } + + getControl(): IEditor | undefined { return this.editorControl; } @@ -296,7 +303,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { // have been applied to the editor directly. let editorSettingsToApply = editorConfiguration; if (this.lastAppliedEditorOptions) { - editorSettingsToApply = objects.distinct(this.lastAppliedEditorOptions, editorSettingsToApply); + editorSettingsToApply = distinct(this.lastAppliedEditorOptions, editorSettingsToApply); } if (Object.keys(editorSettingsToApply).length > 0) { diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index d7e9666628f..866bb82c0f2 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as types from 'vs/base/common/types'; +import { assertIsDefined, isFunction } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; @@ -19,7 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { Event } from 'vs/base/common/event'; -import { ScrollType } from 'vs/editor/common/editorCommon'; +import { ScrollType, IEditor } from 'vs/editor/common/editorCommon'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -74,36 +74,37 @@ export class AbstractTextResourceEditor extends BaseTextEditor { } // Set Editor Model - const textEditor = this.getControl(); + const textEditor = assertIsDefined(this.getControl()); const textEditorModel = resolvedModel.textEditorModel; textEditor.setModel(textEditorModel); // Apply Options from TextOptions let optionsGotApplied = false; const textOptions = options; - if (textOptions && types.isFunction(textOptions.apply)) { + if (textOptions && isFunction(textOptions.apply)) { optionsGotApplied = textOptions.apply(textEditor, ScrollType.Immediate); } // Otherwise restore View State if (!optionsGotApplied) { - this.restoreTextResourceEditorViewState(input); + this.restoreTextResourceEditorViewState(input, textEditor); } } - private restoreTextResourceEditorViewState(input: EditorInput) { - if (input instanceof UntitledEditorInput || input instanceof ResourceEditorInput) { - const viewState = this.loadTextEditorViewState(input.getResource()); + private restoreTextResourceEditorViewState(editor: EditorInput, control: IEditor) { + if (editor instanceof UntitledEditorInput || editor instanceof ResourceEditorInput) { + const viewState = this.loadTextEditorViewState(editor.getResource()); if (viewState) { - this.getControl().restoreViewState(viewState); + control.restoreViewState(viewState); } } } setOptions(options: EditorOptions | undefined): void { const textOptions = options; - if (textOptions && types.isFunction(textOptions.apply)) { - textOptions.apply(this.getControl(), ScrollType.Smooth); + if (textOptions && isFunction(textOptions.apply)) { + const textEditor = assertIsDefined(this.getControl()); + textOptions.apply(textEditor, ScrollType.Smooth); } } @@ -149,7 +150,10 @@ export class AbstractTextResourceEditor extends BaseTextEditor { this.saveTextResourceEditorViewState(this.input); // Clear Model - this.getControl().setModel(null); + const textEditor = this.getControl(); + if (textEditor) { + textEditor.setModel(null); + } super.clearInput(); } diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 089bbbc8875..30b2652363e 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -39,7 +39,7 @@ import { Themable } from 'vs/workbench/common/theme'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IFileService } from 'vs/platform/files/common/files'; -import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; +import { withNullAsUndefined, withUndefinedAsNull, assertIsDefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; export interface IToolbarActions { @@ -57,7 +57,7 @@ export abstract class TitleControl extends Themable { private currentPrimaryEditorActionIds: string[] = []; private currentSecondaryEditorActionIds: string[] = []; - private editorActionsToolbar: ToolBar; + private editorActionsToolbar: ToolBar | undefined; private resourceContext: ResourceContextKey; private editorPinnedContext: IContextKey; @@ -188,7 +188,8 @@ export abstract class TitleControl extends Themable { primaryEditorActions.some(action => action instanceof ExecuteCommandAction) || // execute command actions can have the same ID but different arguments secondaryEditorActions.some(action => action instanceof ExecuteCommandAction) // see also https://github.com/Microsoft/vscode/issues/16298 ) { - this.editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)(); + const editorActionsToolbar = assertIsDefined(this.editorActionsToolbar); + editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)(); this.currentPrimaryEditorActionIds = primaryEditorActionIds; this.currentSecondaryEditorActionIds = secondaryEditorActionIds; @@ -241,7 +242,9 @@ export abstract class TitleControl extends Themable { } protected clearEditorActionsToolbar(): void { - this.editorActionsToolbar.setActions([], [])(); + if (this.editorActionsToolbar) { + this.editorActionsToolbar.setActions([], [])(); + } this.currentPrimaryEditorActionIds = []; this.currentSecondaryEditorActionIds = []; @@ -257,7 +260,9 @@ export abstract class TitleControl extends Themable { // Set editor group as transfer this.groupTransfer.setData([new DraggedEditorGroupIdentifier(this.group.id)], DraggedEditorGroupIdentifier.prototype); - e.dataTransfer!.effectAllowed = 'copyMove'; + if (e.dataTransfer) { + e.dataTransfer.effectAllowed = 'copyMove'; + } // If tabs are disabled, treat dragging as if an editor tab was dragged if (!this.accessor.partOptions.showTabs) { diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index 4a000267835..8150f9673ea 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -38,6 +38,7 @@ height: 22px; margin-right: 4px; margin-left: 4px; + font-size: 18px; background-position: center; background-repeat: no-repeat; } @@ -70,8 +71,7 @@ } .monaco-workbench .notifications-list-container .notification-list-item:hover .notification-list-item-toolbar-container, -.monaco-workbench .notifications-list-container .monaco-list-row.focused .notification-list-item .notification-list-item-toolbar-container, -.monaco-workbench .notifications-list-container .notification-list-item.expanded .notification-list-item-toolbar-container { +.monaco-workbench .notifications-list-container .monaco-list-row.focused .notification-list-item .notification-list-item-toolbar-container { display: block; } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts index c2c688d9073..94bfac3f5cf 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts @@ -43,7 +43,7 @@ export class ClearAllNotificationsAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, 'codicon-close-all'); + super(id, label, 'codicon-clear-all'); } run(notification: INotificationViewItem): Promise { @@ -63,7 +63,7 @@ export class HideNotificationsCenterAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, 'codicon-chevron-down'); + super(id, label, 'codicon-close'); } run(notification: INotificationViewItem): Promise { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index a0077787b11..b3444fa21e7 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -22,21 +22,23 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ClearAllNotificationsAction, HideNotificationsCenterAction, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions'; import { IAction } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { assertAllDefined, assertIsDefined } from 'vs/base/common/types'; export class NotificationsCenter extends Themable { - private static MAX_DIMENSIONS = new Dimension(450, 400); + private static readonly MAX_DIMENSIONS = new Dimension(450, 400); private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - private notificationsCenterContainer: HTMLElement; - private notificationsCenterHeader: HTMLElement; - private notificationsCenterTitle: HTMLSpanElement; - private notificationsList: NotificationsList; - private _isVisible: boolean; - private workbenchDimensions: Dimension; + private notificationsCenterContainer: HTMLElement | undefined; + private notificationsCenterHeader: HTMLElement | undefined; + private notificationsCenterTitle: HTMLSpanElement | undefined; + private notificationsList: NotificationsList | undefined; + private _isVisible: boolean | undefined; + private workbenchDimensions: Dimension | undefined; private notificationsCenterVisibleContextKey: IContextKey; + private clearAllAction: ClearAllNotificationsAction | undefined; constructor( private container: HTMLElement, @@ -61,12 +63,13 @@ export class NotificationsCenter extends Themable { } get isVisible(): boolean { - return this._isVisible; + return !!this._isVisible; } show(): void { if (this._isVisible) { - this.notificationsList.show(true /* focus */); + const notificationsList = assertIsDefined(this.notificationsList); + notificationsList.show(true /* focus */); return; // already visible } @@ -80,18 +83,19 @@ export class NotificationsCenter extends Themable { this.updateTitle(); // Make visible + const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer); this._isVisible = true; - addClass(this.notificationsCenterContainer, 'visible'); - this.notificationsList.show(); + addClass(notificationsCenterContainer, 'visible'); + notificationsList.show(); // Layout this.layout(this.workbenchDimensions); // Show all notifications that are present now - this.notificationsList.updateNotificationsList(0, 0, this.model.notifications); + notificationsList.updateNotificationsList(0, 0, this.model.notifications); // Focus first - this.notificationsList.focusFirst(); + notificationsList.focusFirst(); // Theming this.updateStyles(); @@ -104,10 +108,14 @@ export class NotificationsCenter extends Themable { } private updateTitle(): void { + const [notificationsCenterTitle, clearAllAction] = assertAllDefined(this.notificationsCenterTitle, this.clearAllAction); + if (this.model.notifications.length === 0) { - this.notificationsCenterTitle.textContent = localize('notificationsEmpty', "No new notifications"); + notificationsCenterTitle.textContent = localize('notificationsEmpty', "No new notifications"); + clearAllAction.enabled = false; } else { - this.notificationsCenterTitle.textContent = localize('notifications', "Notifications"); + notificationsCenterTitle.textContent = localize('notifications', "Notifications"); + clearAllAction.enabled = true; } } @@ -139,12 +147,12 @@ export class NotificationsCenter extends Themable { actionRunner })); + this.clearAllAction = this._register(this.instantiationService.createInstance(ClearAllNotificationsAction, ClearAllNotificationsAction.ID, ClearAllNotificationsAction.LABEL)); + notificationsToolBar.push(this.clearAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.clearAllAction) }); + const hideAllAction = this._register(this.instantiationService.createInstance(HideNotificationsCenterAction, HideNotificationsCenterAction.ID, HideNotificationsCenterAction.LABEL)); notificationsToolBar.push(hideAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(hideAllAction) }); - const clearAllAction = this._register(this.instantiationService.createInstance(ClearAllNotificationsAction, ClearAllNotificationsAction.ID, ClearAllNotificationsAction.LABEL)); - notificationsToolBar.push(clearAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(clearAllAction) }); - // Notifications List this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, { ariaLabel: localize('notificationsList', "Notifications List") @@ -167,16 +175,17 @@ export class NotificationsCenter extends Themable { let focusGroup = false; // Update notifications list based on event + const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer); switch (e.kind) { case NotificationChangeType.ADD: - this.notificationsList.updateNotificationsList(e.index, 0, [e.item]); + notificationsList.updateNotificationsList(e.index, 0, [e.item]); break; case NotificationChangeType.CHANGE: - this.notificationsList.updateNotificationsList(e.index, 1, [e.item]); + notificationsList.updateNotificationsList(e.index, 1, [e.item]); break; case NotificationChangeType.REMOVE: - focusGroup = isAncestor(document.activeElement, this.notificationsCenterContainer); - this.notificationsList.updateNotificationsList(e.index, 1); + focusGroup = isAncestor(document.activeElement, notificationsCenterContainer); + notificationsList.updateNotificationsList(e.index, 1); break; } @@ -195,7 +204,7 @@ export class NotificationsCenter extends Themable { } hide(): void { - if (!this._isVisible || !this.notificationsCenterContainer) { + if (!this._isVisible || !this.notificationsCenterContainer || !this.notificationsList) { return; // already hidden } @@ -219,22 +228,22 @@ export class NotificationsCenter extends Themable { } protected updateStyles(): void { - if (this.notificationsCenterContainer) { + if (this.notificationsCenterContainer && this.notificationsCenterHeader) { const widgetShadowColor = this.getColor(widgetShadow); - this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null; + this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : ''; const borderColor = this.getColor(NOTIFICATIONS_CENTER_BORDER); - this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : null; + this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : ''; const headerForeground = this.getColor(NOTIFICATIONS_CENTER_HEADER_FOREGROUND); this.notificationsCenterHeader.style.color = headerForeground ? headerForeground.toString() : null; const headerBackground = this.getColor(NOTIFICATIONS_CENTER_HEADER_BACKGROUND); - this.notificationsCenterHeader.style.background = headerBackground ? headerBackground.toString() : null; + this.notificationsCenterHeader.style.background = headerBackground ? headerBackground.toString() : ''; } } - layout(dimension: Dimension): void { + layout(dimension: Dimension | undefined): void { this.workbenchDimensions = dimension; if (this._isVisible && this.notificationsCenterContainer) { @@ -264,7 +273,8 @@ export class NotificationsCenter extends Themable { } // Apply to list - this.notificationsList.layout(Math.min(maxWidth, availableWidth), Math.min(maxHeight, availableHeight)); + const notificationsList = assertIsDefined(this.notificationsList); + notificationsList.layout(Math.min(maxWidth, availableWidth), Math.min(maxHeight, availableHeight)); } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 3c13be13ad2..7a83faeacfb 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -16,10 +16,11 @@ import { NotificationsListDelegate, NotificationRenderer } from 'vs/workbench/br import { NotificationActionRunner, CopyNotificationMessageAction } from 'vs/workbench/browser/parts/notifications/notificationsActions'; import { NotificationFocusedContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; export class NotificationsList extends Themable { - private listContainer: HTMLElement; - private list: WorkbenchList; + private listContainer: HTMLElement | undefined; + private list: WorkbenchList | undefined; private viewModel: INotificationViewItem[]; private isVisible: boolean | undefined; @@ -38,7 +39,8 @@ export class NotificationsList extends Themable { show(focus?: boolean): void { if (this.isVisible) { if (focus) { - this.list.domFocus(); + const list = assertIsDefined(this.list); + list.domFocus(); } return; // already visible @@ -54,7 +56,8 @@ export class NotificationsList extends Themable { // Focus if (focus) { - this.list.domFocus(); + const list = assertIsDefined(this.list); + list.domFocus(); } } @@ -70,7 +73,7 @@ export class NotificationsList extends Themable { const renderer = this.instantiationService.createInstance(NotificationRenderer, actionRunner); // List - this.list = this._register(this.instantiationService.createInstance( + const list = this.list = this._register(this.instantiationService.createInstance( WorkbenchList, 'NotificationsList', this.listContainer, @@ -85,13 +88,13 @@ export class NotificationsList extends Themable { // Context menu to copy message const copyAction = this._register(this.instantiationService.createInstance(CopyNotificationMessageAction, CopyNotificationMessageAction.ID, CopyNotificationMessageAction.LABEL)); - this._register((this.list.onContextMenu(e => { + this._register((list.onContextMenu(e => { if (!e.element) { return; } this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor!, + getAnchor: () => e.anchor, getActions: () => [copyAction], getActionsContext: () => e.element, actionRunner @@ -99,27 +102,27 @@ export class NotificationsList extends Themable { }))); // Toggle on double click - this._register((this.list.onMouseDblClick(event => (event.element as INotificationViewItem).toggle()))); + this._register((list.onMouseDblClick(event => (event.element as INotificationViewItem).toggle()))); // Clear focus when DOM focus moves out // Use document.hasFocus() to not clear the focus when the entire window lost focus // This ensures that when the focus comes back, the notification is still focused - const listFocusTracker = this._register(trackFocus(this.list.getHTMLElement())); + const listFocusTracker = this._register(trackFocus(list.getHTMLElement())); this._register(listFocusTracker.onDidBlur(() => { if (document.hasFocus()) { - this.list.setFocus([]); + list.setFocus([]); } })); // Context key - NotificationFocusedContext.bindTo(this.list.contextKeyService); + NotificationFocusedContext.bindTo(list.contextKeyService); // Only allow for focus in notifications, as the // selection is too strong over the contents of // the notification - this._register(this.list.onSelectionChange(e => { + this._register(list.onSelectionChange(e => { if (e.indexes.length > 0) { - this.list.setSelection([]); + list.setSelection([]); } })); @@ -129,23 +132,24 @@ export class NotificationsList extends Themable { } updateNotificationsList(start: number, deleteCount: number, items: INotificationViewItem[] = []) { - const listHasDOMFocus = isAncestor(document.activeElement, this.listContainer); + const [list, listContainer] = assertAllDefined(this.list, this.listContainer); + const listHasDOMFocus = isAncestor(document.activeElement, listContainer); // Remember focus and relative top of that item - const focusedIndex = this.list.getFocus()[0]; + const focusedIndex = list.getFocus()[0]; const focusedItem = this.viewModel[focusedIndex]; let focusRelativeTop: number | null = null; if (typeof focusedIndex === 'number') { - focusRelativeTop = this.list.getRelativeTop(focusedIndex); + focusRelativeTop = list.getRelativeTop(focusedIndex); } // Update view model this.viewModel.splice(start, deleteCount, ...items); // Update list - this.list.splice(start, deleteCount, items); - this.list.layout(); + list.splice(start, deleteCount, items); + list.layout(); // Hide if no more notifications to show if (this.viewModel.length === 0) { @@ -167,15 +171,15 @@ export class NotificationsList extends Themable { } if (typeof focusRelativeTop === 'number') { - this.list.reveal(indexToFocus, focusRelativeTop); + list.reveal(indexToFocus, focusRelativeTop); } - this.list.setFocus([indexToFocus]); + list.setFocus([indexToFocus]); } // Restore DOM focus if we had focus before if (listHasDOMFocus) { - this.list.domFocus(); + list.domFocus(); } } @@ -204,7 +208,7 @@ export class NotificationsList extends Themable { } hasFocus(): boolean { - if (!this.isVisible || !this.list) { + if (!this.isVisible || !this.listContainer) { return false; // hidden } @@ -217,7 +221,7 @@ export class NotificationsList extends Themable { this.listContainer.style.color = foreground ? foreground.toString() : null; const background = this.getColor(NOTIFICATIONS_BACKGROUND); - this.listContainer.style.background = background ? background.toString() : null; + this.listContainer.style.background = background ? background.toString() : ''; const outlineColor = this.getColor(contrastBorder); this.listContainer.style.outlineColor = outlineColor ? outlineColor.toString() : ''; @@ -225,7 +229,7 @@ export class NotificationsList extends Themable { } layout(width: number, maxHeight?: number): void { - if (this.list) { + if (this.listContainer && this.list) { this.listContainer.style.width = `${width}px`; if (typeof maxHeight === 'number') { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 4be947506f2..863dd01c1c7 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -18,11 +18,12 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { localize } from 'vs/nls'; -import { Severity } from 'vs/platform/notification/common/notification'; +import { Severity, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { timeout } from 'vs/base/common/async'; +import { assertIsDefined } from 'vs/base/common/types'; interface INotificationToast { item: INotificationViewItem; @@ -40,8 +41,8 @@ enum ToastVisibility { export class NotificationsToasts extends Themable { - private static MAX_WIDTH = 450; - private static MAX_NOTIFICATIONS = 3; + private static readonly MAX_WIDTH = 450; + private static readonly MAX_NOTIFICATIONS = 3; private static PURGE_TIMEOUT: { [severity: number]: number } = (() => { const intervals = Object.create(null); @@ -52,8 +53,8 @@ export class NotificationsToasts extends Themable { return intervals; })(); - private notificationsToastsContainer: HTMLElement; - private workbenchDimensions: Dimension; + private notificationsToastsContainer: HTMLElement | undefined; + private workbenchDimensions: Dimension | undefined; private isNotificationsCenterVisible: boolean | undefined; private mapNotificationToToast: Map; private notificationsToastsVisibleContextKey: IContextKey; @@ -91,6 +92,13 @@ export class NotificationsToasts extends Themable { // Update toasts on notification changes this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e))); }); + + // Filter + this._register(this.model.onDidFilterChange(filter => { + if (filter === NotificationsFilter.SILENT) { + this.hide(); + } + })); } private async onCanShowNotifications(): Promise { @@ -125,15 +133,16 @@ export class NotificationsToasts extends Themable { } // Lazily create toasts containers - if (!this.notificationsToastsContainer) { - this.notificationsToastsContainer = document.createElement('div'); - addClass(this.notificationsToastsContainer, 'notifications-toasts'); + let notificationsToastsContainer = this.notificationsToastsContainer; + if (!notificationsToastsContainer) { + notificationsToastsContainer = this.notificationsToastsContainer = document.createElement('div'); + addClass(notificationsToastsContainer, 'notifications-toasts'); - this.container.appendChild(this.notificationsToastsContainer); + this.container.appendChild(notificationsToastsContainer); } // Make Visible - addClass(this.notificationsToastsContainer, 'visible'); + addClass(notificationsToastsContainer, 'visible'); const itemDisposables = new DisposableStore(); @@ -141,11 +150,11 @@ export class NotificationsToasts extends Themable { const notificationToastContainer = document.createElement('div'); addClass(notificationToastContainer, 'notification-toast-container'); - const firstToast = this.notificationsToastsContainer.firstChild; + const firstToast = notificationsToastsContainer.firstChild; if (firstToast) { - this.notificationsToastsContainer.insertBefore(notificationToastContainer, firstToast); // always first + notificationsToastsContainer.insertBefore(notificationToastContainer, firstToast); // always first } else { - this.notificationsToastsContainer.appendChild(notificationToastContainer); + notificationsToastsContainer.appendChild(notificationToastContainer); } // Toast @@ -164,8 +173,8 @@ export class NotificationsToasts extends Themable { this.mapNotificationToToast.set(item, toast); itemDisposables.add(toDisposable(() => { - if (this.isVisible(toast)) { - this.notificationsToastsContainer.removeChild(toast.container); + if (this.isVisible(toast) && notificationsToastsContainer) { + notificationsToastsContainer.removeChild(toast.container); } })); @@ -318,7 +327,7 @@ export class NotificationsToasts extends Themable { } hide(): void { - const focusGroup = isAncestor(document.activeElement, this.notificationsToastsContainer); + const focusGroup = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false; this.removeToasts(); @@ -412,10 +421,10 @@ export class NotificationsToasts extends Themable { protected updateStyles(): void { this.mapNotificationToToast.forEach(t => { const widgetShadowColor = this.getColor(widgetShadow); - t.toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null; + t.toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : ''; const borderColor = this.getColor(NOTIFICATIONS_TOAST_BORDER); - t.toast.style.border = borderColor ? `1px solid ${borderColor}` : null; + t.toast.style.border = borderColor ? `1px solid ${borderColor}` : ''; }); } @@ -443,7 +452,7 @@ export class NotificationsToasts extends Themable { return notificationToasts.reverse(); // from newest to oldest } - layout(dimension: Dimension): void { + layout(dimension: Dimension | undefined): void { this.workbenchDimensions = dimension; const maxDimensions = this.computeMaxDimensions(); @@ -525,10 +534,11 @@ export class NotificationsToasts extends Themable { return; } + const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer); if (visible) { - this.notificationsToastsContainer.appendChild(toast.container); + notificationsToastsContainer.appendChild(toast.container); } else { - this.notificationsToastsContainer.removeChild(toast.container); + notificationsToastsContainer.removeChild(toast.container); } } diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index c8419eed7e7..30de9340067 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -21,7 +21,7 @@ import { ActivePanelContext, PanelPositionContext } from 'vs/workbench/common/pa export class ClosePanelAction extends Action { static readonly ID = 'workbench.action.closePanel'; - static LABEL = nls.localize('closePanel', "Close Panel"); + static readonly LABEL = nls.localize('closePanel', "Close Panel"); constructor( id: string, @@ -40,7 +40,7 @@ export class ClosePanelAction extends Action { export class TogglePanelAction extends Action { static readonly ID = 'workbench.action.togglePanel'; - static LABEL = nls.localize('togglePanel', "Toggle Panel"); + static readonly LABEL = nls.localize('togglePanel', "Toggle Panel"); constructor( id: string, @@ -209,7 +209,7 @@ export class SwitchPanelViewAction extends Action { export class PreviousPanelViewAction extends SwitchPanelViewAction { static readonly ID = 'workbench.action.previousPanelView'; - static LABEL = nls.localize('previousPanelView', 'Previous Panel View'); + static readonly LABEL = nls.localize('previousPanelView', 'Previous Panel View'); constructor( id: string, @@ -227,7 +227,7 @@ export class PreviousPanelViewAction extends SwitchPanelViewAction { export class NextPanelViewAction extends SwitchPanelViewAction { static readonly ID = 'workbench.action.nextPanelView'; - static LABEL = nls.localize('nextPanelView', 'Next Panel View'); + static readonly LABEL = nls.localize('nextPanelView', 'Next Panel View'); constructor( id: string, diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 103e0075498..f9a66d07a6b 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -30,7 +30,7 @@ import { Dimension, trackFocus } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { isUndefinedOrNull, withUndefinedAsNull, withNullAsUndefined } from 'vs/base/common/types'; +import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -60,18 +60,18 @@ export class PanelPart extends CompositePart implements IPanelService { readonly snap = true; get preferredHeight(): number | undefined { - const sidebarDimension = this.layoutService.getDimension(Parts.SIDEBAR_PART); - return sidebarDimension.height * 0.4; + // Don't worry about titlebar or statusbar visibility + // The difference is minimal and keeps this function clean + return this.layoutService.dimension.height * 0.4; } get preferredWidth(): number | undefined { - const statusbarPart = this.layoutService.getDimension(Parts.STATUSBAR_PART); - return statusbarPart.width * 0.4; + return this.layoutService.dimension.width * 0.4; } //#endregion - get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); } + get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean; }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); } readonly onDidPanelClose: Event = this.onDidCompositeClose.event; private _onDidVisibilityChange = this._register(new Emitter()); @@ -81,7 +81,7 @@ export class PanelPart extends CompositePart implements IPanelService { private panelFocusContextKey: IContextKey; private compositeBar: CompositeBar; - private compositeActions: Map = new Map(); + private compositeActions: Map = new Map(); private blockOpeningPanel = false; private _contentDimension: Dimension | undefined; @@ -123,7 +123,7 @@ export class PanelPart extends CompositePart implements IPanelService { openComposite: (compositeId: string) => Promise.resolve(this.openPanel(compositeId, true)), getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction, getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction, - getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)), + getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, assertIsDefined(this.getPanel(compositeId))), getContextMenuActions: () => [ this.instantiationService.createInstance(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel")) @@ -208,19 +208,19 @@ export class PanelPart extends CompositePart implements IPanelService { updateStyles(): void { super.updateStyles(); - const container = this.getContainer(); - container.style.backgroundColor = this.getColor(PANEL_BACKGROUND); - container.style.borderLeftColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder); + const container = assertIsDefined(this.getContainer()); + container.style.backgroundColor = this.getColor(PANEL_BACKGROUND) || ''; + container.style.borderLeftColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder) || ''; const title = this.getTitleArea(); if (title) { - title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder); + title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder) || ''; } } - openPanel(id: string, focus?: boolean): Panel | null { + openPanel(id: string, focus?: boolean): Panel | undefined { if (this.blockOpeningPanel) { - return null; // Workaround against a potential race condition + return undefined; // Workaround against a potential race condition } // First check if panel is hidden and show if so @@ -233,7 +233,7 @@ export class PanelPart extends CompositePart implements IPanelService { } } - return withUndefinedAsNull(this.openComposite(id, focus)); + return this.openComposite(id, focus); } showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable { @@ -241,15 +241,15 @@ export class PanelPart extends CompositePart implements IPanelService { } getPanel(panelId: string): IPanelIdentifier | undefined { - return withNullAsUndefined(Registry.as(PanelExtensions.Panels).getPanel(panelId)); + return Registry.as(PanelExtensions.Panels).getPanel(panelId); } - getPanels(): PanelDescriptor[] { + getPanels(): readonly PanelDescriptor[] { return Registry.as(PanelExtensions.Panels).getPanels() .sort((v1, v2) => typeof v1.order === 'number' && typeof v2.order === 'number' ? v1.order - v2.order : NaN); } - getPinnedPanels(): PanelDescriptor[] { + getPinnedPanels(): readonly PanelDescriptor[] { const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(c => c.id); return this.getPanels() .filter(p => pinnedCompositeIds.indexOf(p.id) !== -1) @@ -263,7 +263,7 @@ export class PanelPart extends CompositePart implements IPanelService { ]; } - getActivePanel(): IPanel | null { + getActivePanel(): IPanel | undefined { return this.getActiveComposite(); } @@ -303,9 +303,9 @@ export class PanelPart extends CompositePart implements IPanelService { } if (this.layoutService.getPanelPosition() === Position.RIGHT) { - this._contentDimension = new Dimension(width - 1, height!); // Take into account the 1px border when layouting + this._contentDimension = new Dimension(width - 1, height); // Take into account the 1px border when layouting } else { - this._contentDimension = new Dimension(width, height!); + this._contentDimension = new Dimension(width, height); } // Layout contents @@ -316,7 +316,7 @@ export class PanelPart extends CompositePart implements IPanelService { } private layoutCompositeBar(): void { - if (this._contentDimension) { + if (this._contentDimension && this.dimension) { let availableWidth = this._contentDimension.width - 40; // take padding into account if (this.toolBar) { availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); // adjust height for global actions showing @@ -326,11 +326,11 @@ export class PanelPart extends CompositePart implements IPanelService { } } - private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } { + private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction; } { let compositeActions = this.compositeActions.get(compositeId); if (!compositeActions) { compositeActions = { - activityAction: this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)), + activityAction: this.instantiationService.createInstance(PanelActivityAction, assertIsDefined(this.getPanel(compositeId))), pinnedAction: new ToggleCompositePinnedAction(this.getPanel(compositeId), this.compositeBar) }; @@ -357,7 +357,7 @@ export class PanelPart extends CompositePart implements IPanelService { private getToolbarWidth(): number { const activePanel = this.getActivePanel(); - if (!activePanel) { + if (!activePanel || !this.toolBar) { return 0; } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 32c171d44a0..24a0b6e3202 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -305,8 +305,8 @@ class QuickInput extends Disposable implements IQuickInput { this.ui.inputBox.showDecoration(severity); if (severity === Severity.Error) { const styles = this.ui.inputBox.stylesForType(severity); - this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : null; - this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : null; + this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : ''; + this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : ''; this.ui.message.style.paddingBottom = '4px'; } else { this.ui.message.style.backgroundColor = ''; @@ -323,7 +323,7 @@ class QuickInput extends Disposable implements IQuickInput { class QuickPick extends QuickInput implements IQuickPick { - private static INPUT_BOX_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); + private static readonly INPUT_BOX_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); private _value = ''; private _placeholder: string; @@ -762,7 +762,7 @@ class QuickPick extends QuickInput implements IQuickPi class InputBox extends QuickInput implements IInputBox { - private static noPromptMessage = localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel"); + private static readonly noPromptMessage = localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel"); private _value = ''; private _valueSelection: Readonly<[number, number]>; @@ -1511,16 +1511,16 @@ export class QuickInputService extends Component implements IQuickInputService { if (this.ui) { // TODO const titleColor = { dark: 'rgba(255, 255, 255, 0.105)', light: 'rgba(0,0,0,.06)', hc: 'black' }[theme.type]; - this.titleBar.style.backgroundColor = titleColor ? titleColor.toString() : null; + this.titleBar.style.backgroundColor = titleColor ? titleColor.toString() : ''; this.ui.inputBox.style(theme); const quickInputBackground = theme.getColor(QUICK_INPUT_BACKGROUND); - this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : null; + this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : ''; const quickInputForeground = theme.getColor(QUICK_INPUT_FOREGROUND); this.ui.container.style.color = quickInputForeground ? quickInputForeground.toString() : null; const contrastBorderColor = theme.getColor(contrastBorder); - this.ui.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null; + this.ui.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : ''; const widgetShadowColor = theme.getColor(widgetShadow); - this.ui.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null; + this.ui.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : ''; } } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 4c992ffd94f..e94290fee50 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -159,7 +159,7 @@ class ListElementRenderer implements IListRenderer implements IViewletService { @@ -58,7 +59,6 @@ export class SidebarPart extends CompositePart implements IViewletServi } const width = viewlet.getOptimalWidth(); - if (typeof width !== 'number') { return; } @@ -173,19 +173,19 @@ export class SidebarPart extends CompositePart implements IViewletServi super.updateStyles(); // Part container - const container = this.getContainer(); + const container = assertIsDefined(this.getContainer()); - container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND); + container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND) || ''; container.style.color = this.getColor(SIDE_BAR_FOREGROUND); const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder); const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT; - container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null; - container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null; - container.style.borderRightColor = isPositionLeft ? borderColor : null; - container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : null; - container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : null; - container.style.borderLeftColor = !isPositionLeft ? borderColor : null; + container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : ''; + container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : ''; + container.style.borderRightColor = isPositionLeft ? borderColor || '' : ''; + container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : ''; + container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : ''; + container.style.borderLeftColor = !isPositionLeft ? borderColor || '' : ''; } layout(width: number, height: number): void { @@ -198,7 +198,7 @@ export class SidebarPart extends CompositePart implements IViewletServi // Viewlet service - getActiveViewlet(): IViewlet | null { + getActiveViewlet(): IViewlet | undefined { return this.getActiveComposite(); } @@ -210,7 +210,7 @@ export class SidebarPart extends CompositePart implements IViewletServi this.hideActiveComposite(); } - async openViewlet(id: string | undefined, focus?: boolean): Promise { + async openViewlet(id: string | undefined, focus?: boolean): Promise { if (typeof id === 'string' && this.getViewlet(id)) { return this.doOpenViewlet(id, focus); } @@ -221,12 +221,21 @@ export class SidebarPart extends CompositePart implements IViewletServi return this.doOpenViewlet(id, focus); } - return null; + return undefined; } getViewlets(): ViewletDescriptor[] { - return this.viewletRegistry.getViewlets() - .sort((v1, v2) => v1.order! - v2.order!); + return this.viewletRegistry.getViewlets().sort((v1, v2) => { + if (typeof v1.order !== 'number') { + return -1; + } + + if (typeof v2.order !== 'number') { + return 1; + } + + return v1.order - v2.order; + }); } getDefaultViewletId(): string { @@ -237,9 +246,9 @@ export class SidebarPart extends CompositePart implements IViewletServi return this.getViewlets().filter(viewlet => viewlet.id === id)[0]; } - private doOpenViewlet(id: string, focus?: boolean): Viewlet | null { + private doOpenViewlet(id: string, focus?: boolean): Viewlet | undefined { if (this.blockOpeningViewlet) { - return null; // Workaround against a potential race condition + return undefined; // Workaround against a potential race condition } // First check if sidebar is hidden and show if so @@ -308,7 +317,7 @@ class FocusSideBarAction extends Action { } // Focus into active viewlet - let viewlet = this.viewletService.getActiveViewlet(); + const viewlet = this.viewletService.getActiveViewlet(); if (viewlet) { viewlet.focus(); } diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css index 92c49501535..b6c02890789 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -10,6 +10,7 @@ height: 22px; font-size: 12px; display: flex; + overflow: visible; } .monaco-workbench .part.statusbar.status-border-top::after { @@ -52,7 +53,7 @@ .monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { content: ''; position: absolute; - left: calc(50% - 8px); /* 3px (margin) + 5px (padding) = 8px */ + left: calc(50% - 9px); /* 3px (margin) + 5px (padding) + 1px (icon) = 9px */ top: -5px; border-bottom-width: 5px; border-bottom-style: solid; diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 769c08aef05..970279684f1 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -27,11 +27,12 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { coalesce } from 'vs/base/common/arrays'; +import { coalesce, find } from 'vs/base/common/arrays'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { values } from 'vs/base/common/map'; +import { assertIsDefined } from 'vs/base/common/types'; interface IPendingStatusbarEntry { id: string; @@ -175,13 +176,7 @@ class StatusbarViewModel extends Disposable { } findEntry(container: HTMLElement): IStatusbarViewModelEntry | undefined { - for (const entry of this._entries) { - if (entry.container === container) { - return entry; - } - } - - return undefined; + return find(this._entries, entry => entry.container === container); } getEntries(alignment: StatusbarAlignment): IStatusbarViewModelEntry[] { @@ -238,7 +233,10 @@ class StatusbarViewModel extends Disposable { return entryB.priority - entryA.priority; // higher priority towards the left } - return mapEntryToIndex.get(entryA)! - mapEntryToIndex.get(entryB)!; // otherwise maintain stable order + const indexA = mapEntryToIndex.get(entryA); + const indexB = mapEntryToIndex.get(entryB); + + return indexA! - indexB!; // otherwise maintain stable order (both values known to be in map) } if (entryA.alignment === StatusbarAlignment.LEFT) { @@ -310,8 +308,8 @@ class ToggleStatusbarEntryVisibilityAction extends Action { class HideStatusbarEntryAction extends Action { - constructor(id: string, private model: StatusbarViewModel) { - super(id, nls.localize('hide', "Hide"), undefined, true); + constructor(id: string, name: string, private model: StatusbarViewModel) { + super(id, nls.localize('hide', "Hide '{0}'", name), undefined, true); } run(): Promise { @@ -334,14 +332,14 @@ export class StatusbarPart extends Part implements IStatusbarService { //#endregion - private styleElement!: HTMLStyleElement; + private styleElement: HTMLStyleElement | undefined; private pendingEntries: IPendingStatusbarEntry[] = []; private readonly viewModel: StatusbarViewModel; - private leftItemsContainer!: HTMLElement; - private rightItemsContainer!: HTMLElement; + private leftItemsContainer: HTMLElement | undefined; + private rightItemsContainer: HTMLElement | undefined; constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -475,7 +473,7 @@ export class StatusbarPart extends Part implements IStatusbarService { ...this.viewModel.getEntries(StatusbarAlignment.LEFT), ...this.viewModel.getEntries(StatusbarAlignment.RIGHT).reverse() // reversing due to flex: row-reverse ].forEach(entry => { - const target = entry.alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer; + const target = assertIsDefined(entry.alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer); target.appendChild(entry.container); }); @@ -488,7 +486,7 @@ export class StatusbarPart extends Part implements IStatusbarService { entries.reverse(); // reversing due to flex: row-reverse } - const target = alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer; + const target = assertIsDefined(alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer); // find an entry that has lower priority than the new one // and then insert the item before that one @@ -562,7 +560,7 @@ export class StatusbarPart extends Part implements IStatusbarService { if (statusEntryUnderMouse) { actions.push(new Separator()); - actions.push(new HideStatusbarEntryAction(statusEntryUnderMouse.id, this.viewModel)); + actions.push(new HideStatusbarEntryAction(statusEntryUnderMouse.id, statusEntryUnderMouse.name, this.viewModel)); } return actions; @@ -571,10 +569,10 @@ export class StatusbarPart extends Part implements IStatusbarService { updateStyles(): void { super.updateStyles(); - const container = this.getContainer(); + const container = assertIsDefined(this.getContainer()); // Background colors - const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND); + const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND) || ''; container.style.backgroundColor = backgroundColor; container.style.color = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND); @@ -691,8 +689,9 @@ class StatusbarEntryItem extends Disposable { if (!this.entry || entry.command !== this.entry.command) { this.commandListener.clear(); - if (entry.command) { - this.commandListener.value = addDisposableListener(this.labelContainer, EventType.CLICK, () => this.executeCommand(entry.command!, entry.arguments)); + const command = entry.command; + if (command) { + this.commandListener.value = addDisposableListener(this.labelContainer, EventType.CLICK, () => this.executeCommand(command, entry.arguments)); removeClass(this.labelContainer, 'disabled'); } else { @@ -779,7 +778,7 @@ class StatusbarEntryItem extends Disposable { } if (isBackground) { - container.style.backgroundColor = colorResult; + container.style.backgroundColor = colorResult || ''; } else { container.style.color = colorResult; } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 2b1d303976e..992ecca3013 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -106,54 +106,29 @@ .monaco-workbench.fullscreen .part.titlebar > .window-controls-container { display: none; + background-color: transparent; } -.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon { display: inline-block; - -webkit-app-region: no-drag; + line-height: 30px; height: 100%; - width: 33.34%; + width: 46px; + font-size: 16px; } -.monaco-workbench .part.titlebar > .window-controls-container .window-icon svg { - shape-rendering: crispEdges; - text-align: center; -} - -.monaco-workbench .part.titlebar.titlebar > .window-controls-container .window-close { - -webkit-mask: url('chrome-close.svg') no-repeat 50% 50%; -} - -.monaco-workbench .part.titlebar.titlebar > .window-controls-container .window-unmaximize { - -webkit-mask: url('chrome-restore.svg') no-repeat 50% 50%; -} - -.monaco-workbench .part.titlebar > .window-controls-container .window-maximize { - -webkit-mask: url('chrome-maximize.svg') no-repeat 50% 50%; -} - -.monaco-workbench .part.titlebar > .window-controls-container .window-minimize { - -webkit-mask: url('chrome-minimize.svg') no-repeat 50% 50%; -} - -.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg > .window-icon { - height: 100%; - width: 100%; - -webkit-mask-size: 23.1%; -} - -.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg:hover { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon:hover { background-color: rgba(255, 255, 255, 0.1); } -.monaco-workbench .part.titlebar.light > .window-controls-container > .window-icon-bg:hover { +.monaco-workbench .part.titlebar.light > .window-controls-container > .window-icon:hover { background-color: rgba(0, 0, 0, 0.1); } -.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg.window-close-bg:hover { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon.window-close:hover { background-color: rgba(232, 17, 35, 0.9); } .monaco-workbench .part.titlebar > .window-controls-container .window-icon.window-close:hover { - background-color: white; + color: white; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 112067306da..095b1c53211 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -81,7 +81,7 @@ export abstract class MenubarControl extends Disposable { protected menuUpdater: RunOnceScheduler; - protected static MAX_MENU_RECENT_ENTRIES = 10; + protected static readonly MAX_MENU_RECENT_ENTRIES = 10; constructor( protected readonly menuService: IMenuService, @@ -570,10 +570,10 @@ export class CustomMenubarControl extends MenubarControl { for (let action of actions) { this.insertActionsBefore(action, target); if (action instanceof SubmenuItemAction) { - if (!this.menus[action.item.submenu]) { - this.menus[action.item.submenu] = this.menuService.createMenu(action.item.submenu, this.contextKeyService); - const submenu = this.menus[action.item.submenu]; - this._register(submenu!.onDidChange(() => { + let submenu = this.menus[action.item.submenu]; + if (!submenu) { + submenu = this.menus[action.item.submenu] = this.menuService.createMenu(action.item.submenu, this.contextKeyService); + this._register(submenu.onDidChange(() => { if (!this.focusInsideMenubar) { const actions: IAction[] = []; updateActions(menu, actions, topLevelTitle); @@ -582,7 +582,6 @@ export class CustomMenubarControl extends MenubarControl { }, this)); } - const submenu = this.menus[action.item.submenu]!; const submenuActions: SubmenuAction[] = []; updateActions(submenu, submenuActions, topLevelTitle); target.push(new SubmenuAction(mnemonicMenuLabel(action.label), submenuActions)); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index d064cc008d6..55bbb16d9ed 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -18,7 +18,7 @@ import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; import * as nls from 'vs/nls'; import { EditorInput, toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme'; import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform'; @@ -288,7 +288,15 @@ export class TitlebarPart extends Part implements ITitleService { // Compute folder resource // Single Root Workspace: always the root single workspace in this case // Otherwise: root folder of the currently active file if any - const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })!); + let folder: IWorkspaceFolder | null = null; + if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + folder = workspace.folders[0]; + } else { + const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + if (resource) { + folder = this.contextService.getWorkspaceFolder(resource); + } + } // Variables const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : ''; @@ -338,6 +346,11 @@ export class TitlebarPart extends Part implements ITitleService { } private installMenubar(): void { + // If the menubar is already installed, skip + if (this.menubar) { + return; + } + this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); this.menubar = this.element.insertBefore($('div.menubar'), this.title); @@ -400,17 +413,13 @@ export class TitlebarPart extends Part implements ITitleService { this.windowControls = append(this.element, $('div.window-controls-container')); // Minimize - const minimizeIconContainer = append(this.windowControls, $('div.window-icon-bg')); - const minimizeIcon = append(minimizeIconContainer, $('div.window-icon')); - addClass(minimizeIcon, 'window-minimize'); + const minimizeIcon = append(this.windowControls, $('div.window-icon.window-minimize.codicon.codicon-chrome-minimize')); this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => { this.electronService.minimizeWindow(); })); // Restore - const restoreIconContainer = append(this.windowControls, $('div.window-icon-bg')); - this.maxRestoreControl = append(restoreIconContainer, $('div.window-icon')); - addClass(this.maxRestoreControl, 'window-max-restore'); + this.maxRestoreControl = append(this.windowControls, $('div.window-icon.window-max-restore.codicon')); this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async e => { const maximized = await this.electronService.isMaximized(); if (maximized) { @@ -421,10 +430,7 @@ export class TitlebarPart extends Part implements ITitleService { })); // Close - const closeIconContainer = append(this.windowControls, $('div.window-icon-bg')); - addClass(closeIconContainer, 'window-close-bg'); - const closeIcon = append(closeIconContainer, $('div.window-icon')); - addClass(closeIcon, 'window-close'); + const closeIcon = append(this.windowControls, $('div.window-icon.window-close.codicon.codicon-chrome-close')); this._register(addDisposableListener(closeIcon, EventType.CLICK, e => { this.electronService.closeWindow(); })); @@ -464,11 +470,11 @@ export class TitlebarPart extends Part implements ITitleService { private onDidChangeMaximized(maximized: boolean) { if (this.maxRestoreControl) { if (maximized) { - removeClass(this.maxRestoreControl, 'window-maximize'); - addClass(this.maxRestoreControl, 'window-unmaximize'); + removeClass(this.maxRestoreControl, 'codicon-chrome-maximize'); + addClass(this.maxRestoreControl, 'codicon-chrome-restore'); } else { - removeClass(this.maxRestoreControl, 'window-unmaximize'); - addClass(this.maxRestoreControl, 'window-maximize'); + removeClass(this.maxRestoreControl, 'codicon-chrome-restore'); + addClass(this.maxRestoreControl, 'codicon-chrome-maximize'); } } @@ -494,7 +500,7 @@ export class TitlebarPart extends Part implements ITitleService { removeClass(this.element, 'inactive'); } - const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND); + const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND) || ''; this.element.style.backgroundColor = titleBackground; if (titleBackground && Color.fromHex(titleBackground).isLighter()) { addClass(this.element, 'light'); @@ -506,7 +512,7 @@ export class TitlebarPart extends Part implements ITitleService { this.element.style.color = titleForeground; const titleBorder = this.getColor(TITLE_BAR_BORDER); - this.element.style.borderBottom = titleBorder ? `1px solid ${titleBorder}` : null; + this.element.style.borderBottom = titleBorder ? `1px solid ${titleBorder}` : ''; } } @@ -546,8 +552,8 @@ export class TitlebarPart extends Part implements ITitleService { // Center between menu and window controls if (leftMarker > (this.element.clientWidth - this.title.clientWidth) / 2 || rightMarker < (this.element.clientWidth + this.title.clientWidth) / 2) { - this.title.style.position = null; - this.title.style.left = null; + this.title.style.position = ''; + this.title.style.left = ''; this.title.style.transform = ''; return; } @@ -608,7 +614,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (titlebarActiveFg) { collector.addRule(` .monaco-workbench .part.titlebar > .window-controls-container .window-icon { - background-color: ${titlebarActiveFg}; + color: ${titlebarActiveFg}; } `); } @@ -617,7 +623,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (titlebarInactiveFg) { collector.addRule(` .monaco-workbench .part.titlebar.inactive > .window-controls-container .window-icon { - background-color: ${titlebarInactiveFg}; + color: ${titlebarInactiveFg}; } `); } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 03c709a0e06..7872aae6f0c 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -753,6 +753,23 @@ class TreeRenderer extends Disposable implements ITreeRenderer { + if ((Math.abs(start) > label.length) || (Math.abs(end) >= label.length)) { + return ({ start: 0, end: 0 }); + } + if (start < 0) { + start = label.length + start; + } + if (end < 0) { + end = label.length + end; + } + if (start > end) { + const swap = start; + start = end; + end = swap; + } + return ({ start, end }); + }) : undefined; const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark; const iconUrl = icon ? URI.revive(icon) : null; const title = node.tooltip ? node.tooltip : resource ? undefined : label; @@ -762,9 +779,9 @@ class TreeRenderer extends Disposable implements ITreeRenderer('explorer.decorations'); - templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: createMatches(element.filterData) }); + templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) }); } else { - templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: createMatches(element.filterData) }); + templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) }); } templateData.icon.style.backgroundImage = iconUrl ? DOM.asCSSUrl(iconUrl) : ''; diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index fabd5818d2c..7a6e3e450ce 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -29,6 +29,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IView, FocusedViewContext } from 'vs/workbench/common/views'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { assertIsDefined } from 'vs/base/common/types'; export interface IPanelColors extends IColorMapping { dropBackground?: ColorIdentifier; @@ -46,7 +47,7 @@ export interface IViewletPanelOptions extends IPanelOptions { export abstract class ViewletPanel extends Panel implements IView { - private static AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions'; + private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions'; private _onDidFocus = this._register(new Emitter()); readonly onDidFocus: Event = this._onDidFocus.event; @@ -67,11 +68,11 @@ export abstract class ViewletPanel extends Panel implements IView { readonly title: string; protected actionRunner?: IActionRunner; - protected toolbar: ToolBar; + protected toolbar?: ToolBar; private readonly showActionsAlways: boolean = false; - private headerContainer: HTMLElement; - private titleContainer: HTMLElement; - protected twistiesContainer: HTMLElement; + private headerContainer?: HTMLElement; + private titleContainer?: HTMLElement; + protected twistiesContainer?: HTMLElement; constructor( options: IViewletPanelOptions, @@ -165,7 +166,9 @@ export abstract class ViewletPanel extends Panel implements IView { } protected updateTitle(title: string): void { - this.titleContainer.textContent = title; + if (this.titleContainer) { + this.titleContainer.textContent = title; + } this._onDidChangeTitleArea.fire(); } @@ -177,11 +180,16 @@ export abstract class ViewletPanel extends Panel implements IView { } private setActions(): void { - this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))(); - this.toolbar.context = this.getActionsContext(); + if (this.toolbar) { + this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))(); + this.toolbar.context = this.getActionsContext(); + } } private updateActionsVisibility(): void { + if (!this.headerContainer) { + return; + } const shouldAlwaysShowActions = this.configurationService.getValue('workbench.view.alwaysShowHeaderActions'); toggleClass(this.headerContainer, 'actions-always-visible', shouldAlwaysShowActions); } @@ -229,10 +237,10 @@ export class PanelViewlet extends Viewlet { private lastFocusedPanel: ViewletPanel | undefined; private panelItems: IViewletPanelItem[] = []; - private panelview: PanelView; + private panelview?: PanelView; get onDidSashChange(): Event { - return this.panelview.onDidSashChange; + return assertIsDefined(this.panelview).onDidSashChange; } protected get panels(): ViewletPanel[] { @@ -274,7 +282,7 @@ export class PanelViewlet extends Viewlet { event.stopPropagation(); event.preventDefault(); - let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; + let anchor: { x: number, y: number; } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActions: () => this.getContextMenuActions() @@ -332,7 +340,9 @@ export class PanelViewlet extends Viewlet { } layout(dimension: Dimension): void { - this.panelview.layout(dimension.height, dimension.width); + if (this.panelview) { + this.panelview.layout(dimension.height, dimension.width); + } } getOptimalWidth(): number { @@ -342,7 +352,7 @@ export class PanelViewlet extends Viewlet { return Math.max(...sizes); } - addPanels(panels: { panel: ViewletPanel, size: number, index?: number }[]): void { + addPanels(panels: { panel: ViewletPanel, size: number, index?: number; }[]): void { const wasSingleView = this.isSingleView(); for (const { panel, size, index } of panels) { @@ -378,7 +388,7 @@ export class PanelViewlet extends Viewlet { const panelItem: IViewletPanelItem = { panel, disposable }; this.panelItems.splice(index, 0, panelItem); - this.panelview.addPanel(panel, size, index); + assertIsDefined(this.panelview).addPanel(panel, size, index); } removePanels(panels: ViewletPanel[]): void { @@ -403,7 +413,7 @@ export class PanelViewlet extends Viewlet { this.lastFocusedPanel = undefined; } - this.panelview.removePanel(panel); + assertIsDefined(this.panelview).removePanel(panel); const [panelItem] = this.panelItems.splice(index, 1); panelItem.disposable.dispose(); @@ -424,15 +434,15 @@ export class PanelViewlet extends Viewlet { const [panelItem] = this.panelItems.splice(fromIndex, 1); this.panelItems.splice(toIndex, 0, panelItem); - this.panelview.movePanel(from, to); + assertIsDefined(this.panelview).movePanel(from, to); } resizePanel(panel: ViewletPanel, size: number): void { - this.panelview.resizePanel(panel, size); + assertIsDefined(this.panelview).resizePanel(panel, size); } getPanelSize(panel: ViewletPanel): number { - return this.panelview.getPanelSize(panel); + return assertIsDefined(this.panelview).getPanelSize(panel); } protected updateViewHeaders(): void { @@ -451,6 +461,8 @@ export class PanelViewlet extends Viewlet { dispose(): void { super.dispose(); this.panelItems.forEach(i => i.disposable.dispose()); - this.panelview.dispose(); + if (this.panelview) { + this.panelview.dispose(); + } } } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 87c08909cff..9a0a307dd5c 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/views'; -import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IViewsService, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -22,14 +22,14 @@ import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/th import { toggleClass, addClass } from 'vs/base/browser/dom'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event { +function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[]; }>): Event { return Event.chain(event) .map(({ views, viewContainer }) => viewContainer === container ? views : []) .filter(views => views.length > 0) .event; } -function filterViewMoveEvent(container: ViewContainer, event: Event<{ from: ViewContainer, to: ViewContainer, views: IViewDescriptor[] }>): Event<{ added?: IViewDescriptor[], removed?: IViewDescriptor[] }> { +function filterViewMoveEvent(container: ViewContainer, event: Event<{ from: ViewContainer, to: ViewContainer, views: IViewDescriptor[]; }>): Event<{ added?: IViewDescriptor[], removed?: IViewDescriptor[]; }> { return Event.chain(event) .map(({ views, from, to }) => from === container ? { removed: views } : to === container ? { added: views } : {}) .filter(({ added, removed }) => isNonEmptyArray(added) || isNonEmptyArray(removed)) @@ -78,8 +78,8 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl private contextKeys = new CounterSet(); private items: IViewItem[] = []; - private _onDidChange: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[] }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>()); - readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }> = this._onDidChange.event; + private _onDidChange: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>()); + readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChange.event; get activeViewDescriptors(): IViewDescriptor[] { return this.items @@ -356,7 +356,7 @@ export class ContributableViewsModel extends Disposable { return viewDescriptor.workspace ? !!viewState.visibleWorkspace : !!viewState.visibleGlobal; } - private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState } { + private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState; } { for (let i = 0, visibleIndex = 0; i < this.viewDescriptors.length; i++) { const viewDescriptor = this.viewDescriptors[i]; const state = this.viewStates.get(viewDescriptor.id); @@ -436,8 +436,8 @@ export class ContributableViewsModel extends Disposable { (a, b) => a.id === b.id ? 0 : a.id < b.id ? -1 : 1 ).reverse(); - const toRemove: { index: number, viewDescriptor: IViewDescriptor }[] = []; - const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean }[] = []; + const toRemove: { index: number, viewDescriptor: IViewDescriptor; }[] = []; + const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = []; for (const splice of splices) { const startViewDescriptor = this.viewDescriptors[splice.start]; @@ -521,7 +521,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { } private saveWorkspaceViewsStates(): void { - const storedViewsStates: { [id: string]: IStoredWorkspaceViewState } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}')); + const storedViewsStates: { [id: string]: IStoredWorkspaceViewState; } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}')); for (const viewDescriptor of this.viewDescriptors) { const viewState = this.viewStates.get(viewDescriptor.id); if (viewState) { @@ -557,7 +557,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { private static loadViewsStates(workspaceViewsStateStorageId: string, globalViewsStateStorageId: string, storageService: IStorageService): Map { const viewStates = new Map(); - const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState }>JSON.parse(storageService.get(workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}')); + const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState; }>JSON.parse(storageService.get(workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}')); for (const id of Object.keys(workspaceViewsStates)) { const workspaceViewState = workspaceViewsStates[id]; viewStates.set(id, { @@ -636,7 +636,7 @@ export class ViewsService extends Disposable implements IViewsService { _serviceBrand: undefined; - private readonly viewDescriptorCollections: Map; + private readonly viewDescriptorCollections: Map; private readonly viewDisposable: Map; private readonly activeViewContextKeys: Map>; @@ -646,7 +646,7 @@ export class ViewsService extends Disposable implements IViewsService { ) { super(); - this.viewDescriptorCollections = new Map(); + this.viewDescriptorCollections = new Map(); this.viewDisposable = new Map(); this.activeViewContextKeys = new Map>(); @@ -692,13 +692,13 @@ export class ViewsService extends Disposable implements IViewsService { } private onDidRegisterViewContainer(viewContainer: ViewContainer): void { - const viewDescriptorCollection = new ViewDescriptorCollection(viewContainer, this.contextKeyService); - const disposables: IDisposable[] = [viewDescriptorCollection]; + const disposables = new DisposableStore(); + const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(viewContainer, this.contextKeyService)); this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables); - this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: toDisposable(() => dispose(disposables)) }); + this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables }); } private onDidDeregisterViewContainer(viewContainer: ViewContainer): void { @@ -709,7 +709,7 @@ export class ViewsService extends Disposable implements IViewsService { } } - private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[] }): void { + private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void { added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true)); removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); } @@ -717,7 +717,7 @@ export class ViewsService extends Disposable implements IViewsService { private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void { const viewlet = this.viewletService.getViewlet(container.id); for (const viewDescriptor of views) { - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); const command: ICommandAction = { id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) }, @@ -725,9 +725,9 @@ export class ViewsService extends Disposable implements IViewsService { }; const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`); - disposables.push(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true))); + disposables.add(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true))); - disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when })); @@ -745,7 +745,7 @@ export class ViewsService extends Disposable implements IViewsService { }); } - this.viewDisposable.set(viewDescriptor, toDisposable(() => dispose(disposables))); + this.viewDisposable.set(viewDescriptor, disposables); } } diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 76d8302ca88..239514051a2 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -64,6 +64,9 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, layoutService, contextMenuService, telemetryService, themeService, storageService); const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).get(id); + if (!container) { + throw new Error('Could not find container'); + } this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewletStateStorageId)); this.viewletState = this.getMemento(StorageScope.WORKSPACE); diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index 230a461907f..0c4b67f1f29 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as objects from 'vs/base/common/objects'; -import * as arrays from 'vs/base/common/arrays'; -import * as strings from 'vs/base/common/strings'; -import * as types from 'vs/base/common/types'; +import { localize } from 'vs/nls'; +import { mixin, assign } from 'vs/base/common/objects'; +import { first } from 'vs/base/common/arrays'; +import { startsWith } from 'vs/base/common/strings'; +import { isString, assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { Mode, IEntryRunContext, IAutoFocus, IModel, IQuickNavigateConfiguration } from 'vs/base/parts/quickopen/common/quickOpen'; @@ -111,9 +111,9 @@ export class QuickOpenHandler { */ getEmptyLabel(searchString: string): string { if (searchString.length > 0) { - return nls.localize('noResultsMatching', "No results matching"); + return localize('noResultsMatching', "No results matching"); } - return nls.localize('noResultsFound2', "No results found"); + return localize('noResultsFound2', "No results found"); } } @@ -128,9 +128,9 @@ export interface QuickOpenHandlerHelpEntry { */ export class QuickOpenHandlerDescriptor { prefix: string; - description: string; + description?: string; contextKey?: string; - helpEntries: QuickOpenHandlerHelpEntry[]; + helpEntries?: QuickOpenHandlerHelpEntry[]; instantProgress: boolean; private id: string; @@ -145,7 +145,7 @@ export class QuickOpenHandlerDescriptor { this.contextKey = contextKey; this.instantProgress = instantProgress; - if (types.isString(param)) { + if (isString(param)) { this.description = param; } else { this.helpEntries = param; @@ -195,7 +195,7 @@ export interface IQuickOpenRegistry { class QuickOpenRegistry implements IQuickOpenRegistry { private handlers: QuickOpenHandlerDescriptor[] = []; - private defaultHandler: QuickOpenHandlerDescriptor; + private defaultHandler: QuickOpenHandlerDescriptor | undefined; registerQuickOpenHandler(descriptor: QuickOpenHandlerDescriptor): void { this.handlers.push(descriptor); @@ -214,11 +214,11 @@ class QuickOpenRegistry implements IQuickOpenRegistry { } getQuickOpenHandler(text: string): QuickOpenHandlerDescriptor | null { - return text ? (arrays.first(this.handlers, h => strings.startsWith(text, h.prefix)) || null) : null; + return text ? (first(this.handlers, h => startsWith(text, h.prefix)) || null) : null; } getDefaultQuickOpenHandler(): QuickOpenHandlerDescriptor { - return this.defaultHandler; + return assertIsDefined(this.defaultHandler); } } @@ -275,17 +275,17 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick if (input instanceof EditorInput) { let opts = this.getOptions(); if (opts) { - opts = objects.mixin(opts, openOptions, true); + opts = mixin(opts, openOptions, true); } else if (openOptions) { opts = EditorOptions.create(openOptions); } - this.editorService.openEditor(input, types.withNullAsUndefined(opts), sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + this.editorService.openEditor(input, withNullAsUndefined(opts), sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } else { const resourceInput = input; if (openOptions) { - resourceInput.options = objects.assign(resourceInput.options || Object.create(null), openOptions); + resourceInput.options = assign(resourceInput.options || Object.create(null), openOptions); } this.editorService.openEditor(resourceInput, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index 368e56b41c7..6ddf85eb74b 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -21,6 +21,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStorageService } from 'vs/platform/storage/common/storage'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { AbstractTree } from 'vs/base/browser/ui/tree/abstractTree'; +import { assertIsDefined } from 'vs/base/common/types'; export abstract class Viewlet extends Composite implements IViewlet { @@ -34,8 +35,8 @@ export abstract class Viewlet extends Composite implements IViewlet { super(id, telemetryService, themeService, storageService); } - getOptimalWidth(): number | null { - return null; + getOptimalWidth(): number | undefined { + return undefined; } getContextMenuActions(): IAction[] { @@ -76,7 +77,7 @@ export const Extensions = { }; export class ViewletRegistry extends CompositeRegistry { - private defaultViewletId!: string; + private defaultViewletId: string | undefined; /** * Registers a viewlet to the platform. @@ -120,7 +121,7 @@ export class ViewletRegistry extends CompositeRegistry { * Gets the id of the viewlet that should open on startup by default. */ getDefaultViewletId(): string { - return this.defaultViewletId; + return assertIsDefined(this.defaultViewletId); } } @@ -166,8 +167,9 @@ export class ShowViewletAction extends Action { private sidebarHasFocus(): boolean { const activeViewlet = this.viewletService.getActiveViewlet(); const activeElement = document.activeElement; + const sidebarPart = this.layoutService.getContainer(Parts.SIDEBAR_PART); - return !!(activeViewlet && activeElement && DOM.isAncestor(activeElement, this.layoutService.getContainer(Parts.SIDEBAR_PART))); + return !!(activeViewlet && activeElement && sidebarPart && DOM.isAncestor(activeElement, sidebarPart)); } } diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 84c46faa362..51194632b7d 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -288,14 +288,6 @@ class BrowserMain extends Disposable { let workspace: IWorkspace | undefined = undefined; if (this.configuration.workspaceProvider) { workspace = this.configuration.workspaceProvider.workspace; - } else { - // TODO@ben remove me once IWorkspaceProvider API is adopted - const legacyConfiguration = this.configuration as { workspaceUri?: URI, folderUri?: URI }; - if (legacyConfiguration.workspaceUri) { - workspace = { workspaceUri: legacyConfiguration.workspaceUri }; - } else if (legacyConfiguration.folderUri) { - workspace = { folderUri: legacyConfiguration.folderUri }; - } } // Multi-root workspace @@ -311,7 +303,7 @@ class BrowserMain extends Disposable { return { id: 'empty-window' }; } - private getRemoteUserDataUri(): URI | null { + private getRemoteUserDataUri(): URI | undefined { const element = document.getElementById('vscode-remote-user-data-uri'); if (element) { const remoteUserDataPath = element.getAttribute('data-settings'); @@ -320,7 +312,7 @@ class BrowserMain extends Disposable { } } - return null; + return undefined; } } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index acc3d4b0129..df67efd0a94 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -364,6 +364,11 @@ import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common 'type': 'boolean', 'default': false, 'description': nls.localize('zenMode.restore', "Controls whether a window should restore to zen mode if it was exited in zen mode.") + }, + 'zenMode.silentNotifications': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.silentNotifications', "Controls whether notifications are shown while in zen mode. If true, all notifications are silent and they will only appear in the status bar.") } } }); diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 917a1ec5d0f..cea697d588d 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -19,7 +19,7 @@ import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/ import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions'; import { Position, Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IStorageService, WillSaveStateReason, StorageScope, IWillSaveStateEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, WillSaveStateReason, StorageScope } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -227,6 +227,20 @@ export class Workbench extends Layout { configurationService: IConfigurationService ): void { + // Configuration changes + this._register(configurationService.onDidChangeConfiguration(() => this.setFontAliasing(configurationService))); + + // Font Info + if (isNative) { + this._register(storageService.onWillSaveState(e => { + if (e.reason === WillSaveStateReason.SHUTDOWN) { + this.storeFontInfo(storageService); + } + })); + } else { + this._register(lifecycleService.onWillShutdown(() => this.storeFontInfo(storageService))); + } + // Lifecycle this._register(lifecycleService.onBeforeShutdown(event => this._onBeforeShutdown.fire(event))); this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event))); @@ -234,12 +248,6 @@ export class Workbench extends Layout { this._onShutdown.fire(); this.dispose(); })); - - // Configuration changes - this._register(configurationService.onDidChangeConfiguration(() => this.setFontAliasing(configurationService))); - - // Storage - this._register(storageService.onWillSaveState(e => this.storeFontInfo(e, storageService))); } private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto' | undefined; @@ -279,13 +287,19 @@ export class Workbench extends Layout { readFontInfo(BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel())); } - private storeFontInfo(e: IWillSaveStateEvent, storageService: IStorageService): void { - if (e.reason === WillSaveStateReason.SHUTDOWN) { - const serializedFontInfo = serializeFontInfo(); - if (serializedFontInfo) { - const serializedFontInfoRaw = JSON.stringify(serializedFontInfo); + private storeFontInfo(storageService: IStorageService): void { + const serializedFontInfo = serializeFontInfo(); + if (serializedFontInfo) { + const serializedFontInfoRaw = JSON.stringify(serializedFontInfo); - isNative ? storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL) : window.localStorage.setItem('editorFontInfo', serializedFontInfoRaw); + // Font info is very specific to the machine the workbench runs + // on. As such, in the web, we prefer to store this info in + // local storage and not global storage because it would not make + // much sense to synchronize to other machines. + if (isNative) { + storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL); + } else { + window.localStorage.setItem('editorFontInfo', serializedFontInfoRaw); } } } diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index e1bc5735a3a..e2a4ca4d321 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -63,11 +63,11 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry } start(accessor: ServicesAccessor): void { - this.instantiationService = accessor.get(IInstantiationService); - this.lifecycleService = accessor.get(ILifecycleService); + const instantiationService = this.instantiationService = accessor.get(IInstantiationService); + const lifecycleService = this.lifecycleService = accessor.get(ILifecycleService); [LifecyclePhase.Starting, LifecyclePhase.Ready, LifecyclePhase.Restored, LifecyclePhase.Eventually].forEach(phase => { - this.instantiateByPhase(this.instantiationService!, this.lifecycleService!, phase); + this.instantiateByPhase(instantiationService, lifecycleService, phase); }); } @@ -119,7 +119,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry try { instantiationService.createInstance(ctor); } catch (error) { - console.error(`Unable to instantiate workbench contribution ${ctor}.`, error); + console.error(`Unable to instantiate workbench contribution ${(ctor as any).name}.`, error); } } } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index c30bfed1f19..94d7bff9153 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -5,7 +5,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { isUndefinedOrNull, withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; @@ -117,7 +117,7 @@ export interface ITextEditor extends IEditor { /** * Returns the underlying text editor widget of this editor. */ - getControl(): ICodeEditor; + getControl(): ICodeEditor | undefined; } export interface ITextDiffEditor extends IEditor { @@ -125,7 +125,7 @@ export interface ITextDiffEditor extends IEditor { /** * Returns the underlying text editor widget of this editor. */ - getControl(): IDiffEditor; + getControl(): IDiffEditor | undefined; } export interface ITextSideBySideEditor extends IEditor { @@ -499,7 +499,7 @@ export interface IEncodingSupport { /** * Gets the encoding of the input if known. */ - getEncoding(): string; + getEncoding(): string | undefined; /** * Sets the encoding for the input for saving. @@ -827,13 +827,13 @@ export class EditorOptions implements IEditorOptions { * Base Text Editor Options. */ export class TextEditorOptions extends EditorOptions { - private startLineNumber: number; - private startColumn: number; - private endLineNumber: number; - private endColumn: number; + private startLineNumber: number | undefined; + private startColumn: number | undefined; + private endLineNumber: number | undefined; + private endColumn: number | undefined; - private revealInCenterIfOutsideViewport: boolean; - private editorViewState: IEditorViewState | null; + private revealInCenterIfOutsideViewport: boolean | undefined; + private editorViewState: IEditorViewState | undefined; static from(input?: IBaseResourceInput): TextEditorOptions | undefined { if (!input || !input.options) { @@ -901,7 +901,7 @@ export class TextEditorOptions extends EditorOptions { const options = TextEditorOptions.create(settings); // View state - options.editorViewState = editor.saveViewState(); + options.editorViewState = withNullAsUndefined(editor.saveViewState()); return options; } @@ -1084,23 +1084,23 @@ export interface IEditorMemento { } class EditorInputFactoryRegistry implements IEditorInputFactoryRegistry { - private instantiationService: IInstantiationService; - private fileInputFactory: IFileInputFactory; + private instantiationService: IInstantiationService | undefined; + private fileInputFactory: IFileInputFactory | undefined; private readonly editorInputFactoryConstructors: Map> = new Map(); private readonly editorInputFactoryInstances: Map = new Map(); start(accessor: ServicesAccessor): void { - this.instantiationService = accessor.get(IInstantiationService); + const instantiationService = this.instantiationService = accessor.get(IInstantiationService); this.editorInputFactoryConstructors.forEach((ctor, key) => { - this.createEditorInputFactory(key, ctor); + this.createEditorInputFactory(key, ctor, instantiationService); }); this.editorInputFactoryConstructors.clear(); } - private createEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0): void { - const instance = this.instantiationService.createInstance(ctor); + private createEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0, instantiationService: IInstantiationService): void { + const instance = instantiationService.createInstance(ctor); this.editorInputFactoryInstances.set(editorInputId, instance); } @@ -1109,14 +1109,14 @@ class EditorInputFactoryRegistry implements IEditorInputFactoryRegistry { } getFileInputFactory(): IFileInputFactory { - return this.fileInputFactory; + return assertIsDefined(this.fileInputFactory); } registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0): void { if (!this.instantiationService) { this.editorInputFactoryConstructors.set(editorInputId, ctor); } else { - this.createEditorInputFactory(editorInputId, ctor); + this.createEditorInputFactory(editorInputId, ctor, this.instantiationService); } } diff --git a/src/vs/workbench/common/editor/binaryEditorModel.ts b/src/vs/workbench/common/editor/binaryEditorModel.ts index 48c1cf5582c..5f94a8c947a 100644 --- a/src/vs/workbench/common/editor/binaryEditorModel.ts +++ b/src/vs/workbench/common/editor/binaryEditorModel.ts @@ -7,25 +7,27 @@ import { EditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; -import { DataUri } from 'vs/base/common/resources'; +import { DataUri, basename } from 'vs/base/common/resources'; +import { MIME_BINARY } from 'vs/base/common/mime'; /** * An editor model that just represents a resource that can be loaded. */ export class BinaryEditorModel extends EditorModel { - private size: number; + private size: number | undefined; private etag: string | undefined; private readonly mime: string; constructor( private readonly resource: URI, - private readonly name: string, + private readonly name: string | undefined, @IFileService private readonly fileService: IFileService ) { super(); this.resource = resource; this.name = name; + this.mime = MIME_BINARY; if (resource.scheme === Schemas.data) { const metadata = DataUri.parseMetaData(resource); @@ -33,7 +35,10 @@ export class BinaryEditorModel extends EditorModel { this.size = Number(metadata.get(DataUri.META_DATA_SIZE)); } - this.mime = metadata.get(DataUri.META_DATA_MIME)!; + const metadataMime = metadata.get(DataUri.META_DATA_MIME); + if (metadataMime) { + this.mime = metadataMime; + } } } @@ -41,7 +46,7 @@ export class BinaryEditorModel extends EditorModel { * The name of the binary resource. */ getName(): string { - return this.name; + return this.name || basename(this.resource); } /** @@ -54,7 +59,7 @@ export class BinaryEditorModel extends EditorModel { /** * The size of the binary resource if known. */ - getSize(): number { + getSize(): number | undefined { return this.size; } diff --git a/src/vs/workbench/common/editor/diffEditorModel.ts b/src/vs/workbench/common/editor/diffEditorModel.ts index 2f3b4818e2b..f5c17787f90 100644 --- a/src/vs/workbench/common/editor/diffEditorModel.ts +++ b/src/vs/workbench/common/editor/diffEditorModel.ts @@ -11,6 +11,7 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; * and the modified version. */ export class DiffEditorModel extends EditorModel { + protected readonly _originalModel: IEditorModel | null; protected readonly _modifiedModel: IEditorModel | null; @@ -58,4 +59,4 @@ export class DiffEditorModel extends EditorModel { super.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index a1447c830ca..19255049976 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -102,7 +102,7 @@ export class EditorGroup extends Disposable { private focusRecentEditorAfterClose: boolean | undefined; constructor( - labelOrSerializedGroup: ISerializedEditorGroup, + labelOrSerializedGroup: ISerializedEditorGroup | undefined, @IInstantiationService private readonly instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService ) { diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 1d7712d1c4e..ee9605889c4 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; +import { basename } from 'vs/base/common/resources'; /** * A read-only text editor input whos contents are made of the provided resource that points to an existing @@ -21,7 +22,7 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { private modelReference: Promise> | null = null; constructor( - private name: string, + private name: string | undefined, private description: string | undefined, private readonly resource: URI, private preferredMode: string | undefined, @@ -43,7 +44,7 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { } getName(): string { - return this.name; + return this.name || basename(this.resource); } setName(name: string): void { diff --git a/src/vs/workbench/common/editor/textDiffEditorModel.ts b/src/vs/workbench/common/editor/textDiffEditorModel.ts index f18c71fe3c8..f064fc3cab8 100644 --- a/src/vs/workbench/common/editor/textDiffEditorModel.ts +++ b/src/vs/workbench/common/editor/textDiffEditorModel.ts @@ -14,14 +14,17 @@ import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; */ export class TextDiffEditorModel extends DiffEditorModel { - protected readonly _originalModel!: BaseTextEditorModel | null; - protected readonly _modifiedModel!: BaseTextEditorModel | null; + protected readonly _originalModel: BaseTextEditorModel | null; + protected readonly _modifiedModel: BaseTextEditorModel | null; private _textDiffEditorModel: IDiffEditorModel | null = null; constructor(originalModel: BaseTextEditorModel, modifiedModel: BaseTextEditorModel) { super(originalModel, modifiedModel); + this._originalModel = originalModel; + this._modifiedModel = modifiedModel; + this.updateTextDiffEditorModel(); } diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index 1c021309268..a446559f1ed 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -35,9 +35,9 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport constructor( private readonly resource: URI, private readonly _hasAssociatedFilePath: boolean, - private preferredMode: string, - private readonly initialValue: string, - private preferredEncoding: string, + private preferredMode: string | undefined, + private readonly initialValue: string | undefined, + private preferredEncoding: string | undefined, @IInstantiationService private readonly instantiationService: IInstantiationService, @ITextFileService private readonly textFileService: ITextFileService, @ILabelService private readonly labelService: ILabelService @@ -177,7 +177,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport return this.getName(); } - getEncoding(): string { + getEncoding(): string | undefined { if (this.cachedModel) { return this.cachedModel.getEncoding(); } diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 57a77b7267b..152eea5f386 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -33,14 +33,14 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin private dirty: boolean = false; private versionId: number = 0; private readonly contentChangeEventScheduler: RunOnceScheduler; - private configuredEncoding: string; + private configuredEncoding?: string; constructor( - private readonly preferredMode: string, + private readonly preferredMode: string | undefined, private readonly resource: URI, private _hasAssociatedFilePath: boolean, - private readonly initialValue: string, - private preferredEncoding: string, + private readonly initialValue: string | undefined, + private preferredEncoding: string | undefined, @IModeService modeService: IModeService, @IModelService modelService: IModelService, @IBackupFileService private readonly backupFileService: IBackupFileService, @@ -87,7 +87,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin return this.preferredMode; } - getEncoding(): string { + getEncoding(): string | undefined { return this.preferredEncoding || this.configuredEncoding; } diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index 9546898f8c9..614d1e561bd 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; +import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -12,6 +12,7 @@ import { Action } from 'vs/base/common/actions'; import { isErrorWithActions } from 'vs/base/common/errorsWithActions'; import { startsWith } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; +import { find, equals } from 'vs/base/common/arrays'; export interface INotificationsModel { @@ -22,9 +23,12 @@ export interface INotificationsModel { readonly notifications: INotificationViewItem[]; readonly onDidNotificationChange: Event; + readonly onDidFilterChange: Event; addNotification(notification: INotification): INotificationHandle; + setFilter(filter: NotificationsFilter): void; + // // Notifications as Status // @@ -123,20 +127,31 @@ export class NotificationHandle implements INotificationHandle { export class NotificationsModel extends Disposable implements INotificationsModel { - private static NO_OP_NOTIFICATION = new NoOpNotification(); + private static readonly NO_OP_NOTIFICATION = new NoOpNotification(); - private readonly _onDidNotificationChange: Emitter = this._register(new Emitter()); + private readonly _onDidNotificationChange = this._register(new Emitter()); readonly onDidNotificationChange: Event = this._onDidNotificationChange.event; - private readonly _onDidStatusMessageChange: Emitter = this._register(new Emitter()); + private readonly _onDidStatusMessageChange = this._register(new Emitter()); readonly onDidStatusMessageChange: Event = this._onDidStatusMessageChange.event; + private readonly _onDidFilterChange = this._register(new Emitter()); + readonly onDidFilterChange: Event = this._onDidFilterChange.event; + private readonly _notifications: INotificationViewItem[] = []; get notifications(): INotificationViewItem[] { return this._notifications; } private _statusMessage: IStatusMessageViewItem | undefined; get statusMessage(): IStatusMessageViewItem | undefined { return this._statusMessage; } + private filter = NotificationsFilter.OFF; + + setFilter(filter: NotificationsFilter): void { + this.filter = filter; + + this._onDidFilterChange.fire(filter); + } + addNotification(notification: INotification): INotificationHandle { const item = this.createViewItem(notification); if (!item) { @@ -169,19 +184,13 @@ export class NotificationsModel extends Disposable implements INotificationsMode } private findNotification(item: INotificationViewItem): INotificationViewItem | undefined { - for (const notification of this._notifications) { - if (notification.equals(item)) { - return notification; - } - } - - return undefined; + return find(this._notifications, notification => notification.equals(item)); } - private createViewItem(notification: INotification): INotificationViewItem | null { - const item = NotificationViewItem.create(notification); + private createViewItem(notification: INotification): INotificationViewItem | undefined { + const item = NotificationViewItem.create(notification, this.filter); if (!item) { - return null; + return undefined; } // Item Events @@ -385,11 +394,11 @@ export interface INotificationMessage { export class NotificationViewItem extends Disposable implements INotificationViewItem { - private static MAX_MESSAGE_LENGTH = 1000; + private static readonly MAX_MESSAGE_LENGTH = 1000; // Example link: "Some message with [link text](http://link.href)." // RegEx: [, anything not ], ], (, http://|https://|command:, no whitespace) - private static LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi; + private static readonly LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi; private _expanded: boolean | undefined; @@ -405,9 +414,9 @@ export class NotificationViewItem extends Disposable implements INotificationVie private readonly _onDidLabelChange: Emitter = this._register(new Emitter()); readonly onDidLabelChange: Event = this._onDidLabelChange.event; - static create(notification: INotification): INotificationViewItem | null { + static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined { if (!notification || !notification.message || isPromiseCanceledError(notification.message)) { - return null; // we need a message to show + return undefined; // we need a message to show } let severity: Severity; @@ -419,7 +428,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie const message = NotificationViewItem.parseNotificationMessage(notification.message); if (!message) { - return null; // we need a message to show + return undefined; // we need a message to show } let actions: INotificationActions | undefined; @@ -429,7 +438,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie actions = { primary: notification.message.actions }; } - return new NotificationViewItem(severity, notification.sticky, notification.silent, message, notification.source, actions); + return new NotificationViewItem(severity, notification.sticky, notification.silent || filter === NotificationsFilter.SILENT, message, notification.source, actions); } private static parseNotificationMessage(input: NotificationMessage): INotificationMessage | undefined { @@ -641,17 +650,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie const primaryActions = (this._actions && this._actions.primary) || []; const otherPrimaryActions = (other.actions && other.actions.primary) || []; - if (primaryActions.length !== otherPrimaryActions.length) { - return false; - } - - for (let i = 0; i < primaryActions.length; i++) { - if ((primaryActions[i].id + primaryActions[i].label) !== (otherPrimaryActions[i].id + otherPrimaryActions[i].label)) { - return false; - } - } - - return true; + return equals(primaryActions, otherPrimaryActions, (a, b) => (a.id + a.label) === (b.id + b.label)); } } @@ -690,9 +689,9 @@ export class ChoiceAction extends Action { class StatusMessageViewItem { - static create(notification: NotificationMessage, options?: IStatusMessageOptions): IStatusMessageViewItem | null { + static create(notification: NotificationMessage, options?: IStatusMessageOptions): IStatusMessageViewItem | undefined { if (!notification || isPromiseCanceledError(notification)) { - return null; // we need a message to show + return undefined; // we need a message to show } let message: string | undefined; @@ -703,7 +702,7 @@ class StatusMessageViewItem { } if (!message) { - return null; // we need a message to show + return undefined; // we need a message to show } return { message, options }; diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index 53de865d8f1..c509716fc49 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -18,13 +18,13 @@ import { withNullAsUndefined } from 'vs/base/common/types'; export class ResourceContextKey extends Disposable implements IContextKey { - static Scheme = new RawContextKey('resourceScheme', undefined); - static Filename = new RawContextKey('resourceFilename', undefined); - static LangId = new RawContextKey('resourceLangId', undefined); - static Resource = new RawContextKey('resource', undefined); - static Extension = new RawContextKey('resourceExtname', undefined); - static HasResource = new RawContextKey('resourceSet', false); - static IsFileSystemResource = new RawContextKey('isFileSystemResource', false); + static readonly Scheme = new RawContextKey('resourceScheme', undefined); + static readonly Filename = new RawContextKey('resourceFilename', undefined); + static readonly LangId = new RawContextKey('resourceLangId', undefined); + static readonly Resource = new RawContextKey('resource', undefined); + static readonly Extension = new RawContextKey('resourceExtname', undefined); + static readonly HasResource = new RawContextKey('resourceSet', false); + static readonly IsFileSystemResource = new RawContextKey('isFileSystemResource', false); private readonly _resourceKey: IContextKey; private readonly _schemeKey: IContextKey; @@ -180,24 +180,24 @@ export class ResourceGlobMatcher extends Disposable { matches(resource: URI): boolean { const folder = this.contextService.getWorkspaceFolder(resource); - let expressionForRoot: ParsedExpression; + let expressionForRoot: ParsedExpression | undefined; if (folder && this.mapRootToParsedExpression.has(folder.uri.toString())) { - expressionForRoot = this.mapRootToParsedExpression.get(folder.uri.toString())!; + expressionForRoot = this.mapRootToParsedExpression.get(folder.uri.toString()); } else { - expressionForRoot = this.mapRootToParsedExpression.get(ResourceGlobMatcher.NO_ROOT)!; + expressionForRoot = this.mapRootToParsedExpression.get(ResourceGlobMatcher.NO_ROOT); } // If the resource if from a workspace, convert its absolute path to a relative // path so that glob patterns have a higher probability to match. For example // a glob pattern of "src/**" will not match on an absolute path "/folder/src/file.txt" // but can match on "src/file.txt" - let resourcePathToMatch: string; + let resourcePathToMatch: string | undefined; if (folder) { - resourcePathToMatch = relativePath(folder.uri, resource)!; // always uses forward slashes + resourcePathToMatch = relativePath(folder.uri, resource); // always uses forward slashes } else { resourcePathToMatch = resource.fsPath; // TODO@isidor: support non-file URIs } - return !!expressionForRoot(resourcePathToMatch); + return !!expressionForRoot && typeof resourcePathToMatch === 'string' && !!expressionForRoot(resourcePathToMatch); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 7d81c6c5e72..ec9755085ca 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground } from 'vs/platform/theme/common/colorRegistry'; import { Disposable } from 'vs/base/common/lifecycle'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; @@ -235,8 +235,8 @@ export const PANEL_INACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.inactiv }, nls.localize('panelInactiveTitleForeground', "Title color for the inactive panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_ACTIVE_TITLE_BORDER = registerColor('panelTitle.activeBorder', { - dark: PANEL_BORDER, - light: PANEL_BORDER, + dark: PANEL_ACTIVE_TITLE_FOREGROUND, + light: PANEL_ACTIVE_TITLE_FOREGROUND, hc: contrastBorder }, nls.localize('panelActiveTitleBorder', "Border color for the active panel title. Panels are shown below the editor area and contain views like output and integrated terminal.")); @@ -335,8 +335,8 @@ export const ACTIVITY_BAR_FOREGROUND = registerColor('activityBar.foreground', { }, nls.localize('activityBarForeground', "Activity bar item foreground color when it is active. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_INACTIVE_FOREGROUND = registerColor('activityBar.inactiveForeground', { - dark: transparent(ACTIVITY_BAR_FOREGROUND, 0.6), - light: transparent(ACTIVITY_BAR_FOREGROUND, 0.6), + dark: transparent(ACTIVITY_BAR_FOREGROUND, 0.4), + light: transparent(ACTIVITY_BAR_FOREGROUND, 0.4), hc: Color.white }, nls.localize('activityBarInActiveForeground', "Activity bar item foreground color when it is inactive. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); @@ -346,6 +346,18 @@ export const ACTIVITY_BAR_BORDER = registerColor('activityBar.border', { hc: contrastBorder }, nls.localize('activityBarBorder', "Activity bar border color separating to the side bar. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +export const ACTIVITY_BAR_ACTIVE_BORDER = registerColor('activityBar.activeBorder', { + dark: ACTIVITY_BAR_FOREGROUND, + light: ACTIVITY_BAR_FOREGROUND, + hc: null +}, nls.localize('activityBarActiveBorder', "Activity bar border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); + +export const ACTIVITY_BAR_ACTIVE_BACKGROUND = registerColor('activityBar.activeBackground', { + dark: null, + light: null, + hc: null +}, nls.localize('activityBarActiveBackground', "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); + export const ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('activityBar.dropBackground', { dark: Color.white.transparent(0.12), light: Color.white.transparent(0.12), @@ -560,21 +572,21 @@ export const NOTIFICATIONS_BORDER = registerColor('notifications.border', { }, nls.localize('notificationsBorder', "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_ERROR_ICON_FOREGROUND = registerColor('notificationsErrorIcon.foreground', { - dark: '#F48771', - light: '#A1260D', - hc: '#F48771' + dark: editorErrorForeground, + light: editorErrorForeground, + hc: editorErrorForeground }, nls.localize('notificationsErrorIconForeground', "The color used for the notification error icon.")); export const NOTIFICATIONS_WARNING_ICON_FOREGROUND = registerColor('notificationsWarningIcon.foreground', { - dark: '#FFCC00', - light: '#DDB100', - hc: '#FFCC00' + dark: editorWarningForeground, + light: editorWarningForeground, + hc: editorWarningForeground }, nls.localize('notificationsWarningIconForeground', "The color used for the notification warning icon.")); export const NOTIFICATIONS_INFO_ICON_FOREGROUND = registerColor('notificationsInfoIcon.foreground', { - dark: '#75BEFF', - light: '#007ACC', - hc: '#75BEFF' + dark: editorInfoForeground, + light: editorInfoForeground, + hc: editorInfoForeground }, nls.localize('notificationsInfoIconForeground', "The color used for the notification info icon.")); /** diff --git a/src/vs/workbench/common/viewlet.ts b/src/vs/workbench/common/viewlet.ts index ddb8340130c..01fafea5968 100644 --- a/src/vs/workbench/common/viewlet.ts +++ b/src/vs/workbench/common/viewlet.ts @@ -15,5 +15,5 @@ export interface IViewlet extends IComposite { /** * Returns the minimal width needed to avoid any content horizontal truncation */ - getOptimalWidth(): number | null; + getOptimalWidth(): number | undefined; } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 6a4147042e3..2e733ef117c 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -7,7 +7,6 @@ import { Command } from 'vs/editor/common/modes'; import { UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITreeViewDataProvider } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index 1615bf30b86..d17090d01a3 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -26,13 +26,13 @@ const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisibl class CallHierarchyController implements IEditorContribution { - static Id = 'callHierarchy'; + static readonly Id = 'callHierarchy'; static get(editor: ICodeEditor): CallHierarchyController { return editor.getContribution(CallHierarchyController.Id); } - private static _StorageDirection = 'callHierarchy/defaultDirection'; + private static readonly _StorageDirection = 'callHierarchy/defaultDirection'; private readonly _ctxHasProvider: IContextKey; private readonly _ctxIsVisible: IContextKey; diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts index 160662e8ac2..06dbdc22a4b 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts @@ -29,13 +29,13 @@ export interface CallHierarchyItem { } export interface IncomingCall { - source: CallHierarchyItem; - sourceRanges: IRange[]; + from: CallHierarchyItem; + fromRanges: IRange[]; } export interface OutgoingCall { - sourceRanges: IRange[]; - target: CallHierarchyItem; + fromRanges: IRange[]; + to: CallHierarchyItem; } export interface CallHierarchyProvider { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 7e4d4ac6dac..120633c77e4 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -295,29 +295,11 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget { } localDispose.add(value); - // update: title and subtitle - let node: callHTree.Call | undefined = element; - let names = [element.item.name]; - while (node) { - let parent = this._tree.getParentElement(node); - let name: string; - if (parent instanceof callHTree.Call) { - name = parent.item.name; - node = parent; - } else { - name = this._tree.getInput()!.word; - node = undefined; - } - if (this._direction === CallHierarchyDirection.CallsTo) { - names.push(name); - } else { - names.unshift(name); - } - } + // update: title const title = this._direction === CallHierarchyDirection.CallsFrom ? localize('callFrom', "Calls from '{0}'", this._tree.getInput()!.word) : localize('callsTo', "Callers of '{0}'", this._tree.getInput()!.word); - this.setTitle(title, names.join(' → ')); + this.setTitle(title); })); this._disposables.add(this._editor.onMouseDown(e => { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts index dbe1dee06ad..37ab8463eb6 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -9,7 +9,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { symbolKindToCssClass, Location } from 'vs/editor/common/modes'; +import { SymbolKinds, Location } from 'vs/editor/common/modes'; import { hash } from 'vs/base/common/hash'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { Range } from 'vs/editor/common/core/range'; @@ -83,8 +83,8 @@ export class DataSource implements IAsyncDataSource { const outgoingCalls = await provideOutgoingCalls(model, position, CancellationToken.None); for (const call of outgoingCalls) { bucket.push(new Call( - call.target, - call.sourceRanges.map(range => ({ range, uri: model.uri })), + call.to, + call.fromRanges.map(range => ({ range, uri: model.uri })), parent )); } @@ -94,8 +94,8 @@ export class DataSource implements IAsyncDataSource { const incomingCalls = await provideIncomingCalls(model, position, CancellationToken.None); for (const call of incomingCalls) { bucket.push(new Call( - call.source, - call.sourceRanges.map(range => ({ range, uri: call.source.uri })), + call.from, + call.fromRanges.map(range => ({ range, uri: call.from.uri })), parent )); } @@ -122,7 +122,7 @@ class CallRenderingTemplate { export class CallRenderer implements ITreeRenderer { - static id = 'CallRenderer'; + static readonly id = 'CallRenderer'; templateId: string = CallRenderer.id; @@ -140,7 +140,7 @@ export class CallRenderer implements ITreeRenderer { class InstallAction extends Action { static readonly ID = 'workbench.action.installCommandLine'; - static LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName); + static readonly LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName); constructor( id: string, @@ -122,7 +122,7 @@ class InstallAction extends Action { class UninstallAction extends Action { static readonly ID = 'workbench.action.uninstallCommandLine'; - static LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName); + static readonly LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName); constructor( id: string, diff --git a/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts b/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts index 558d193d12a..b0f4b9c4894 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts @@ -43,16 +43,7 @@ interface ILanguageConfiguration { } function isStringArr(something: string[] | null): something is string[] { - if (!Array.isArray(something)) { - return false; - } - for (let i = 0, len = something.length; i < len; i++) { - if (typeof something[i] !== 'string') { - return false; - } - } - return true; - + return Array.isArray(something) && something.every(value => typeof value === 'string'); } function isCharacterPair(something: CharacterPair | null): boolean { diff --git a/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts index ebf9955db2a..5d7a143aa98 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts @@ -5,9 +5,8 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; -import * as process from 'vs/base/common/process'; import * as platform from 'vs/base/common/platform'; -import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -17,7 +16,7 @@ import { EndOfLinePreference } from 'vs/editor/common/model'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; export class SelectionClipboard extends Disposable implements IEditorContribution { - private static SELECTION_LENGTH_LIMIT = 65536; + private static readonly SELECTION_LENGTH_LIMIT = 65536; private static readonly ID = 'editor.contrib.selectionClipboard'; constructor(editor: ICodeEditor, @IClipboardService clipboardService: IClipboardService) { @@ -32,33 +31,12 @@ export class SelectionClipboard extends Disposable implements IEditorContributio } })); - this._register(editor.onMouseDown((e: IEditorMouseEvent) => { + this._register(editor.onMouseUp((e: IEditorMouseEvent) => { if (!isEnabled) { - return; - } - if (!editor.hasModel()) { - return; - } - if (e.event.middleButton) { - e.event.preventDefault(); - editor.focus(); - - if (e.target.position) { - editor.setPosition(e.target.position); + if (e.event.middleButton) { + // try to stop the upcoming paste + e.event.preventDefault(); } - - if (e.target.type === MouseTargetType.SCROLLBAR) { - return; - } - - process.nextTick(() => { - // TODO@Alex: electron weirdness: calling clipboard.readText('selection') generates a paste event, so no need to execute paste ourselves - clipboardService.readText('selection'); - // keybindingService.executeCommand(Handler.Paste, { - // text: clipboard.readText('selection'), - // pasteOnNewLine: false - // }); - }); } })); @@ -99,6 +77,11 @@ export class SelectionClipboard extends Disposable implements IEditorContributio if (!isEnabled) { return; } + if (e.source === 'restoreState') { + // do not set selection to clipboard if this selection change + // was caused by restoring editors... + return; + } setSelectionToClipboard.schedule(); })); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 2ce7aef85f7..924ad443981 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -216,7 +216,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { public style(colors: ISuggestEnabledInputStyles): void { - this.stylingContainer.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : null; + this.stylingContainer.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : ''; this.stylingContainer.style.color = colors.inputForeground ? colors.inputForeground.toString() : null; this.placeholderText.style.color = colors.inputPlaceholderForeground ? colors.inputPlaceholderForeground.toString() : null; @@ -228,7 +228,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { const cursor = this.stylingContainer.getElementsByClassName('cursor')[0] as HTMLDivElement; if (cursor) { - cursor.style.backgroundColor = colors.inputForeground ? colors.inputForeground.toString() : null; + cursor.style.backgroundColor = colors.inputForeground ? colors.inputForeground.toString() : ''; } } @@ -290,6 +290,7 @@ function getSuggestEnabledInputOptions(ariaLabel?: string): IEditorOptions { ariaLabel: ariaLabel || '', snippetSuggestions: 'none', - suggest: { filterGraceful: false, showIcons: false } + suggest: { filterGraceful: false, showIcons: false }, + autoClosingBrackets: 'never' }; } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 09438e54175..a58e626db11 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -100,7 +100,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget editor: ICodeEditor, private _owner: string, private _commentThread: modes.CommentThread, - private _pendingComment: string, + private _pendingComment: string | null, @IInstantiationService private instantiationService: IInstantiationService, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index bd3ea6d7f9d..87f2dd31cc5 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -76,10 +76,7 @@ class CommentingRangeDecoration { options: commentingOptions }]; - let model = this._editor.getModel(); - if (model) { - this._decorationId = model.deltaDecorations([this._decorationId], commentingRangeDecorations)[0]; - } + this._decorationId = this._editor.deltaDecorations([], commentingRangeDecorations)[0]; } public getCommentAction(): { ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges } { diff --git a/src/vs/workbench/contrib/comments/browser/commentsPanel.ts b/src/vs/workbench/contrib/comments/browser/commentsPanel.ts index 5f0e33758a4..eeafa5eb74a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsPanel.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsPanel.ts @@ -27,13 +27,13 @@ import { CommentsList, COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbe export class CommentsPanel extends Panel { - private treeLabels: ResourceLabels; - private tree: CommentsList; - private treeContainer: HTMLElement; - private messageBoxContainer: HTMLElement; - private messageBox: HTMLElement; - private commentsModel: CommentsModel; - private collapseAllAction: IAction; + private treeLabels!: ResourceLabels; + private tree!: CommentsList; + private treeContainer!: HTMLElement; + private messageBoxContainer!: HTMLElement; + private messageBox!: HTMLElement; + private commentsModel!: CommentsModel; + private collapseAllAction?: IAction; constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -195,7 +195,9 @@ export class CommentsPanel extends Panel { private refresh(): void { if (this.isVisible()) { - this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads(); + if (this.collapseAllAction) { + this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads(); + } dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads()); this.tree.updateChildren().then(() => { diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 4cdcb6a6895..299a7271b2b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -56,8 +56,8 @@ interface ICommentThreadTemplateData { } export class CommentsModelVirualDelegate implements IListVirtualDelegate { - private static RESOURCE_ID = 'resource-with-comments'; - private static COMMENT_ID = 'comment-node'; + private static readonly RESOURCE_ID = 'resource-with-comments'; + private static readonly COMMENT_ID = 'comment-node'; getHeight(element: any): number { diff --git a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts index a06e6ce4017..a5ae07ec17f 100644 --- a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts +++ b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts @@ -33,6 +33,10 @@ export class ReactionActionViewItem extends ActionViewItem { super(null, action, {}); } updateLabel(): void { + if (!this.label) { + return; + } + let action = this.getAction() as ReactionAction; if (action.class) { this.label.classList.add(action.class); diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 9137385cc80..996aa3ceb7e 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Schemas } from 'vs/base/common/network'; +import { firstOrDefault } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import * as nls from 'vs/nls'; @@ -12,19 +12,17 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; import { IEditorCommandsContext } from 'vs/workbench/common/editor'; -import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { ICustomEditorService, CONTEXT_HAS_CUSTOM_EDITORS } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { firstOrDefault } from 'vs/base/common/arrays'; const viewCategory = nls.localize('viewCategory', "View"); // #region Open With const OPEN_WITH_COMMAND_ID = 'openWith'; -const OPEN_WITH_TITLE = { value: nls.localize('openWith.title', 'Open With'), original: 'Open With' }; +// const OPEN_WITH_TITLE = { value: nls.localize('openWith.title', 'Open With'), original: 'Open With' }; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: OPEN_WITH_COMMAND_ID, @@ -41,15 +39,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: 'navigation', - order: 20, - command: { - id: OPEN_WITH_COMMAND_ID, - title: OPEN_WITH_TITLE, - }, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) -}); +// MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { +// group: 'navigation', +// order: 20, +// command: { +// id: OPEN_WITH_COMMAND_ID, +// title: OPEN_WITH_TITLE, +// }, +// when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) +// }); // #endregion @@ -95,7 +93,8 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { id: REOPEN_WITH_COMMAND_ID, title: REOPEN_WITH_TITLE, category: viewCategory, - } + }, + when: CONTEXT_HAS_CUSTOM_EDITORS, }); // #endregion diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index ac4bc9dc18d..46ce92e8f15 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -5,26 +5,24 @@ import { memoize } from 'vs/base/common/decorators'; import { Emitter } from 'vs/base/common/event'; +import { Lazy } from 'vs/base/common/lazy'; import { UnownedDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; +import { DataUri, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { WebviewContentState } from 'vs/editor/common/modes'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ILabelService } from 'vs/platform/label/common/label'; import { ConfirmResult, IEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { promptSave } from 'vs/workbench/services/textfile/browser/textFileService'; -export class CustomFileEditorInput extends WebviewInput { +export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { public static typeId = 'workbench.editors.webviewEditor'; - private name?: string; - private _hasResolved = false; private readonly _editorResource: URI; private _state = WebviewContentState.Readonly; @@ -32,13 +30,12 @@ export class CustomFileEditorInput extends WebviewInput { resource: URI, viewType: string, id: string, - webview: UnownedDisposable, - @ILabelService private readonly labelService: ILabelService, - @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService, - @IExtensionService private readonly _extensionService: IExtensionService, + webview: Lazy>, + @IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService, @IDialogService private readonly dialogService: IDialogService, + @ILabelService private readonly labelService: ILabelService, ) { - super(id, viewType, '', webview); + super(id, viewType, '', webview, webviewWorkbenchService); this._editorResource = resource; } @@ -50,17 +47,34 @@ export class CustomFileEditorInput extends WebviewInput { return this._editorResource; } + @memoize getName(): string { - if (!this.name) { - this.name = basename(this.labelService.getUriLabel(this.getResource())); + if (this.getResource().scheme === Schemas.data) { + const metadata = DataUri.parseMetaData(this.getResource()); + const label = metadata.get(DataUri.META_DATA_LABEL); + if (typeof label === 'string') { + return label; + } } - return this.name; + return basename(this.labelService.getUriLabel(this.getResource())); + } + + @memoize + getDescription(): string | undefined { + if (this.getResource().scheme === Schemas.data) { + const metadata = DataUri.parseMetaData(this.getResource()); + const description = metadata.get(DataUri.META_DATA_DESCRIPTION); + if (typeof description === 'string') { + return description; + } + } + return super.getDescription(); } matches(other: IEditorInput): boolean { return this === other || (other instanceof CustomFileEditorInput && this.viewType === other.viewType - && this.getResource().toString() === other.getResource().toString()); + && isEqual(this.getResource(), other.getResource())); } @memoize @@ -70,11 +84,17 @@ export class CustomFileEditorInput extends WebviewInput { @memoize private get mediumTitle(): string { + if (this.getResource().scheme === Schemas.data) { + return this.getName(); + } return this.labelService.getUriLabel(this.getResource(), { relative: true }); } @memoize private get longTitle(): string { + if (this.getResource().scheme === Schemas.data) { + return this.getName(); + } return this.labelService.getUriLabel(this.getResource()); } @@ -90,15 +110,6 @@ export class CustomFileEditorInput extends WebviewInput { } } - public async resolve(): Promise { - if (!this._hasResolved) { - this._hasResolved = true; - this._extensionService.activateByEvent(`onWebviewEditor:${this.viewType}`); - await this._webviewEditorService.resolveWebview(this); - } - return super.resolve(); - } - public setState(newState: WebviewContentState): void { this._state = newState; this._onDidChangeDirty.fire(); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index 6f038e2db50..e4509a59b02 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -9,7 +9,8 @@ import { generateUuid } from 'vs/base/common/uuid'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; +import { Lazy } from 'vs/base/common/lazy'; export class CustomEditoInputFactory extends WebviewEditorInputFactory { @@ -17,9 +18,9 @@ export class CustomEditoInputFactory extends WebviewEditorInputFactory { public constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IWebviewEditorService private readonly webviewService: IWebviewEditorService, + @IWebviewWorkbenchService private readonly webviewWorkbenchService: IWebviewWorkbenchService, ) { - super(webviewService); + super(webviewWorkbenchService); } public serialize(input: CustomFileEditorInput): string | undefined { @@ -41,12 +42,16 @@ export class CustomEditoInputFactory extends WebviewEditorInputFactory { ): CustomFileEditorInput { const data = this.fromJson(serializedEditorInput); const id = data.id || generateUuid(); - const webviewInput = this.webviewService.reviveWebview(id, data.viewType, data.title, data.iconPath, data.state, data.options, data.extensionLocation ? { - location: data.extensionLocation, - id: data.extensionId - } : undefined, data.group); - const customInput = this._instantiationService.createInstance(CustomFileEditorInput, URI.from((data as any).editorResource), data.viewType, id, new UnownedDisposable(webviewInput.webview)); + const webview = new Lazy(() => { + const webviewInput = this.webviewWorkbenchService.reviveWebview(id, data.viewType, data.title, data.iconPath, data.state, data.options, data.extensionLocation && data.extensionId ? { + location: data.extensionLocation, + id: data.extensionId + } : undefined, data.group); + return new UnownedDisposable(webviewInput.webview); + }); + + const customInput = this._instantiationService.createInstance(CustomFileEditorInput, URI.from((data as any).editorResource), data.viewType, id, webview); if (typeof data.group === 'number') { customInput.updateGroup(data.group); } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index a38b00bab20..36eb48e2e48 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -5,7 +5,7 @@ import { coalesce, distinct } from 'vs/base/common/arrays'; import * as glob from 'vs/base/common/glob'; -import { UnownedDisposable } from 'vs/base/common/lifecycle'; +import { UnownedDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { basename, DataUri, isEqual } from 'vs/base/common/resources'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -22,22 +22,24 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { EditorInput, EditorOptions, IEditor, IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { webviewEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/browser/extensionPoint'; -import { CustomEditorDiscretion, CustomEditorInfo, CustomEditorSelector, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { CustomEditorPriority, CustomEditorInfo, CustomEditorSelector, ICustomEditorService, CONTEXT_HAS_CUSTOM_EDITORS } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { CustomFileEditorInput } from './customEditorInput'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { Lazy } from 'vs/base/common/lazy'; const defaultEditorId = 'default'; const defaultEditorInfo: CustomEditorInfo = { id: defaultEditorId, - displayName: nls.localize('promptOpenWith.defaultEditor', "Default built-in editor"), + displayName: nls.localize('promptOpenWith.defaultEditor', "VS Code's standard text editor"), selector: [ { filenamePattern: '*' } ], - discretion: CustomEditorDiscretion.default, + priority: CustomEditorPriority.default, }; export class CustomEditorStore { @@ -67,18 +69,22 @@ export class CustomEditorStore { } } -export class CustomEditorService implements ICustomEditorService { +export class CustomEditorService extends Disposable implements ICustomEditorService { _serviceBrand: any; private readonly editors = new CustomEditorStore(); + private readonly _hasCustomEditor: IContextKey; constructor( + @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IWebviewService private readonly webviewService: IWebviewService, ) { + super(); + webviewEditorsExtensionPoint.setHandler(extensions => { this.editors.clear(); @@ -88,11 +94,17 @@ export class CustomEditorService implements ICustomEditorService { id: webviewEditorContribution.viewType, displayName: webviewEditorContribution.displayName, selector: webviewEditorContribution.selector || [], - discretion: webviewEditorContribution.discretion || CustomEditorDiscretion.default, + priority: webviewEditorContribution.priority || CustomEditorPriority.default, }); } } + this.updateContext(); }); + + this._hasCustomEditor = CONTEXT_HAS_CUSTOM_EDITORS.bindTo(contextKeyService); + + this._register(this.editorService.onDidActiveEditorChange(() => this.updateContext())); + this.updateContext(); } public getContributedCustomEditors(resource: URI): readonly CustomEditorInfo[] { @@ -154,13 +166,15 @@ export class CustomEditorService implements ICustomEditorService { resource: URI, viewType: string, group: IEditorGroup | undefined, - options?: { readonly customClasses: string }, + options?: { readonly customClasses: string; }, ): CustomFileEditorInput { const id = generateUuid(); - const webview = this.webviewService.createWebviewEditorOverlay(id, { customClasses: options ? options.customClasses : undefined }, {}); - const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, new UnownedDisposable(webview)); + const webview = new Lazy(() => { + return new UnownedDisposable(this.webviewService.createWebviewEditorOverlay(id, { customClasses: options ? options.customClasses : undefined }, {})); + }); + const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, webview); if (group) { - input.updateGroup(group!.id); + input.updateGroup(group.id); } return input; } @@ -174,20 +188,45 @@ export class CustomEditorService implements ICustomEditorService { if (group) { const existingEditors = group.editors.filter(editor => editor.getResource() && isEqual(editor.getResource()!, resource)); if (existingEditors.length) { - await this.editorService.replaceEditors([{ - editor: existingEditors[0], - replacement: input, - options: options ? EditorOptions.create(options) : undefined, - }], group); + const existing = existingEditors[0]; + if (!input.matches(existing)) { + await this.editorService.replaceEditors([{ + editor: existing, + replacement: input, + options: options ? EditorOptions.create(options) : undefined, + }], group); + + if (existing instanceof CustomFileEditorInput) { + existing.dispose(); + } + } } } return this.editorService.openEditor(input, options, group); } + + private updateContext() { + const activeControl = this.editorService.activeControl; + if (!activeControl) { + this._hasCustomEditor.reset(); + return; + } + const resource = activeControl.input.getResource(); + if (!resource) { + this._hasCustomEditor.reset(); + return; + } + const possibleEditors = [ + ...this.getContributedCustomEditors(resource), + ...this.getUserConfiguredCustomEditors(resource), + ]; + this._hasCustomEditor.set(possibleEditors.length > 0); + } } export const customEditorsAssociationsKey = 'workbench.experimental.editorAssociations'; -export type CustomEditorsAssociations = readonly (CustomEditorSelector & { readonly viewType: string })[]; +export type CustomEditorsAssociations = readonly (CustomEditorSelector & { readonly viewType: string; })[]; export class CustomEditorContribution implements IWorkbenchContribution { constructor( @@ -203,7 +242,9 @@ export class CustomEditorContribution implements IWorkbenchContribution { group: IEditorGroup ): IOpenEditorOverride | undefined { if (editor instanceof CustomFileEditorInput) { - return undefined; + if (editor.group === group.id) { + return undefined; + } } if (editor instanceof DiffEditorInput) { @@ -231,7 +272,7 @@ export class CustomEditorContribution implements IWorkbenchContribution { return; } - const defaultEditors = contributedEditors.filter(editor => editor.discretion === CustomEditorDiscretion.default); + const defaultEditors = contributedEditors.filter(editor => editor.priority === CustomEditorPriority.default); if (defaultEditors.length === 1) { return { override: this.customEditorService.openWith(resource, defaultEditors[0].id, options, group), diff --git a/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts b/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts index a17205467bb..0ab4cbd7514 100644 --- a/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts +++ b/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts @@ -5,7 +5,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as nls from 'vs/nls'; -import { CustomEditorDiscretion, CustomEditorSelector } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { CustomEditorPriority, CustomEditorSelector } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService'; @@ -13,14 +13,14 @@ namespace WebviewEditorContribution { export const viewType = 'viewType'; export const displayName = 'displayName'; export const selector = 'selector'; - export const discretion = 'discretion'; + export const priority = 'priority'; } interface IWebviewEditorsExtensionPoint { readonly [WebviewEditorContribution.viewType]: string; readonly [WebviewEditorContribution.displayName]: string; readonly [WebviewEditorContribution.selector]?: readonly CustomEditorSelector[]; - readonly [WebviewEditorContribution.discretion]?: CustomEditorDiscretion; + readonly [WebviewEditorContribution.priority]?: CustomEditorPriority; } const webviewEditorsContribution: IJSONSchema = { @@ -41,7 +41,7 @@ const webviewEditorsContribution: IJSONSchema = { }, [WebviewEditorContribution.displayName]: { type: 'string', - description: nls.localize('contributes.displayName', 'Name of the custom editor displayed to users.'), + description: nls.localize('contributes.displayName', 'Human readable name of the custom editor. This is displayed to users when selecting which editor to use.'), }, [WebviewEditorContribution.selector]: { type: 'array', @@ -53,23 +53,23 @@ const webviewEditorsContribution: IJSONSchema = { type: 'string', description: nls.localize('contributes.selector.filenamePattern', 'Glob that the custom editor is enabled for.'), }, - scheme: { + mime: { type: 'string', - description: nls.localize('contributes.selector.scheme', 'File scheme that the custom editor is enabled for.'), + description: nls.localize('contributes.selector.mime', 'Glob that matches the mime type of a data uri resource.'), } } } }, - [WebviewEditorContribution.discretion]: { + [WebviewEditorContribution.priority]: { type: 'string', - description: nls.localize('contributes.discretion', 'Controls when the custom editor is used. May be overridden by users.'), + description: nls.localize('contributes.priority', 'Controls when the custom editor is used. May be overridden by users.'), enum: [ - CustomEditorDiscretion.default, - CustomEditorDiscretion.option + CustomEditorPriority.default, + CustomEditorPriority.option ], enumDescriptions: [ - nls.localize('contributes.discretion.default', 'Editor is automatically used for a resource if no other default custom editors are registered for it.'), - nls.localize('contributes.discretion.option', 'Editor is not automatically used but can be selected by a user.'), + nls.localize('contributes.priority.default', 'Editor is automatically used for a resource if no other default custom editors are registered for it.'), + nls.localize('contributes.priority.option', 'Editor is not automatically used but can be selected by a user.'), ], default: 'default' } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index a6e1c1d4b37..f635727192f 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -8,9 +8,12 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { EditorInput, IEditor } from 'vs/workbench/common/editor'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export const ICustomEditorService = createDecorator('customEditorService'); +export const CONTEXT_HAS_CUSTOM_EDITORS = new RawContextKey('hasCustomEditors', false); + export interface ICustomEditorService { _serviceBrand: any; @@ -23,7 +26,7 @@ export interface ICustomEditorService { promptOpenWith(resource: URI, options?: ITextEditorOptions, group?: IEditorGroup): Promise; } -export const enum CustomEditorDiscretion { +export const enum CustomEditorPriority { default = 'default', option = 'option', } @@ -36,6 +39,6 @@ export interface CustomEditorSelector { export interface CustomEditorInfo { readonly id: string; readonly displayName: string; - readonly discretion: CustomEditorDiscretion; + readonly priority: CustomEditorPriority; readonly selector: readonly CustomEditorSelector[]; } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 80306bf6af8..d2917d2c74f 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -30,6 +30,7 @@ import { memoize } from 'vs/base/common/decorators'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { distinct } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; const $ = dom.$; @@ -542,6 +543,20 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable { onHide: () => dispose(actions) }); })); + + const updateSize = () => { + const lineHeight = this.editor.getOption(EditorOption.lineHeight); + this.domNode.style.height = `${lineHeight}px`; + this.domNode.style.width = `${Math.ceil(0.8 * lineHeight)}px`; + this.domNode.style.marginLeft = `${Math.ceil(0.35 * lineHeight)}px`; + }; + updateSize(); + + this.toDispose.push(this.editor.onDidChangeConfiguration(c => { + if (c.hasChanged(EditorOption.fontSize) || c.hasChanged(EditorOption.lineHeight)) { + updateSize(); + } + })); } @memoize diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 3ba18b6a7f0..dee135687c4 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -54,8 +54,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi private hitCountInput = ''; private logMessageInput = ''; private breakpoint: IBreakpoint | undefined; + private context: Context; - constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, private context: Context, + constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, context: Context | undefined, @IContextViewService private readonly contextViewService: IContextViewService, @IDebugService private readonly debugService: IDebugService, @IThemeService private readonly themeService: IThemeService, @@ -74,7 +75,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.breakpoint = breakpoints.length ? breakpoints[0] : undefined; } - if (this.context === undefined) { + if (context === undefined) { if (this.breakpoint && !this.breakpoint.condition && !this.breakpoint.hitCondition && this.breakpoint.logMessage) { this.context = Context.LOG_MESSAGE; } else if (this.breakpoint && !this.breakpoint.condition && this.breakpoint.hitCondition) { @@ -82,6 +83,8 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } else { this.context = Context.CONDITION; } + } else { + this.context = context; } this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(e => { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 7f8ffb936c3..624002135be 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -14,7 +14,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list'; diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index a42bce188ed..74bccc63e53 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -261,6 +261,7 @@ configurationRegistry.registerConfiguration({ }, 'debug.onTaskErrors': { enum: ['debugAnyway', 'showErrors', 'prompt'], + enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user.")], description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."), default: 'prompt' } diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index 7653bcd4d64..381e6df69cc 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -122,8 +122,8 @@ export class StartDebugActionViewItem implements IActionViewItem { } })); this.toDispose.push(attachStylerCallback(this.themeService, { selectBorder }, colors => { - this.container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : null; - selectBoxContainer.style.borderLeft = colors.selectBorder ? `1px solid ${colors.selectBorder}` : null; + this.container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : ''; + selectBoxContainer.style.borderLeft = colors.selectBorder ? `1px solid ${colors.selectBorder}` : ''; })); this.updateOptions(); diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index 3a2c52b0193..1440892a8f9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -15,20 +15,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { startDebugging } from 'vs/workbench/contrib/debug/common/debugUtils'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; export abstract class AbstractDebugAction extends Action { - protected toDispose: IDisposable[]; - constructor( id: string, label: string, cssClass: string, @IDebugService protected debugService: IDebugService, @IKeybindingService protected keybindingService: IKeybindingService, ) { super(id, label, cssClass, false); - this.toDispose = []; - this.toDispose.push(this.debugService.onDidChangeState(state => this.updateEnablement(state))); + this._register(this.debugService.onDidChangeState(state => this.updateEnablement(state))); this.updateLabel(label); this.updateEnablement(); @@ -56,16 +52,11 @@ export abstract class AbstractDebugAction extends Action { protected isEnabled(_: State): boolean { return true; } - - dispose(): void { - super.dispose(); - this.toDispose = dispose(this.toDispose); - } } export class ConfigureAction extends AbstractDebugAction { static readonly ID = 'workbench.action.debug.configure'; - static LABEL = nls.localize('openLaunchJson', "Open {0}", 'launch.json'); + static readonly LABEL = nls.localize('openLaunchJson', "Open {0}", 'launch.json'); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @@ -74,7 +65,7 @@ export class ConfigureAction extends AbstractDebugAction { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService ) { super(id, label, 'debug-action configure', debugService, keybindingService); - this.toDispose.push(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass())); + this._register(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass())); this.updateClass(); } @@ -120,10 +111,10 @@ export class StartAction extends AbstractDebugAction { ) { super(id, label, 'debug-action start', debugService, keybindingService); - this.toDispose.push(this.debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateEnablement())); - this.toDispose.push(this.debugService.onDidNewSession(() => this.updateEnablement())); - this.toDispose.push(this.debugService.onDidEndSession(() => this.updateEnablement())); - this.toDispose.push(this.contextService.onDidChangeWorkbenchState(() => this.updateEnablement())); + this._register(this.debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateEnablement())); + this._register(this.debugService.onDidNewSession(() => this.updateEnablement())); + this._register(this.debugService.onDidEndSession(() => this.updateEnablement())); + this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateEnablement())); } run(): Promise { @@ -165,7 +156,7 @@ export class RunAction extends StartAction { export class SelectAndStartAction extends AbstractDebugAction { static readonly ID = 'workbench.action.debug.selectandstart'; - static LABEL = nls.localize('selectAndStartDebugging', "Select and Start Debugging"); + static readonly LABEL = nls.localize('selectAndStartDebugging', "Select and Start Debugging"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @@ -182,7 +173,7 @@ export class SelectAndStartAction extends AbstractDebugAction { export class RemoveBreakpointAction extends Action { static readonly ID = 'workbench.debug.viewlet.action.removeBreakpoint'; - static LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint"); + static readonly LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint"); constructor(id: string, label: string, @IDebugService private readonly debugService: IDebugService) { super(id, label, 'debug-action remove'); @@ -196,11 +187,11 @@ export class RemoveBreakpointAction extends Action { export class RemoveAllBreakpointsAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.removeAllBreakpoints'; - static LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints"); + static readonly LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action remove-all', debugService, keybindingService); - this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } run(): Promise { @@ -215,11 +206,11 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { export class EnableAllBreakpointsAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.enableAllBreakpoints'; - static LABEL = nls.localize('enableAllBreakpoints', "Enable All Breakpoints"); + static readonly LABEL = nls.localize('enableAllBreakpoints', "Enable All Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action enable-all-breakpoints', debugService, keybindingService); - this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } run(): Promise { @@ -234,11 +225,11 @@ export class EnableAllBreakpointsAction extends AbstractDebugAction { export class DisableAllBreakpointsAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.disableAllBreakpoints'; - static LABEL = nls.localize('disableAllBreakpoints', "Disable All Breakpoints"); + static readonly LABEL = nls.localize('disableAllBreakpoints', "Disable All Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action disable-all-breakpoints', debugService, keybindingService); - this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } run(): Promise { @@ -253,14 +244,14 @@ export class DisableAllBreakpointsAction extends AbstractDebugAction { export class ToggleBreakpointsActivatedAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.toggleBreakpointsActivatedAction'; - static ACTIVATE_LABEL = nls.localize('activateBreakpoints', "Activate Breakpoints"); - static DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints"); + static readonly ACTIVATE_LABEL = nls.localize('activateBreakpoints', "Activate Breakpoints"); + static readonly DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action breakpoints-activate', debugService, keybindingService); this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL); - this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => { + this._register(this.debugService.getModel().onDidChangeBreakpoints(() => { this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL); this.updateEnablement(); })); @@ -277,11 +268,11 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction { export class ReapplyBreakpointsAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.reapplyBreakpointsAction'; - static LABEL = nls.localize('reapplyAllBreakpoints', "Reapply All Breakpoints"); + static readonly LABEL = nls.localize('reapplyAllBreakpoints', "Reapply All Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, '', debugService, keybindingService); - this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } run(): Promise { @@ -297,11 +288,11 @@ export class ReapplyBreakpointsAction extends AbstractDebugAction { export class AddFunctionBreakpointAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.addFunctionBreakpointAction'; - static LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint"); + static readonly LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action add-function-breakpoint', debugService, keybindingService); - this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); + this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } run(): Promise { @@ -317,12 +308,12 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { export class AddWatchExpressionAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.addWatchExpression'; - static LABEL = nls.localize('addWatchExpression', "Add Expression"); + static readonly LABEL = nls.localize('addWatchExpression', "Add Expression"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action add-watch-expression', debugService, keybindingService); - this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); - this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(() => this.updateEnablement())); + this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); + this._register(this.debugService.getViewModel().onDidSelectExpression(() => this.updateEnablement())); } run(): Promise { @@ -338,11 +329,11 @@ export class AddWatchExpressionAction extends AbstractDebugAction { export class RemoveAllWatchExpressionsAction extends AbstractDebugAction { static readonly ID = 'workbench.debug.viewlet.action.removeAllWatchExpressions'; - static LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions"); + static readonly LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { super(id, label, 'debug-action remove-all', debugService, keybindingService); - this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); + this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); } run(): Promise { @@ -357,7 +348,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction { export class FocusSessionAction extends AbstractDebugAction { static readonly ID = 'workbench.action.debug.focusProcess'; - static LABEL = nls.localize('focusSession', "Focus Session"); + static readonly LABEL = nls.localize('focusSession', "Focus Session"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @@ -367,8 +358,8 @@ export class FocusSessionAction extends AbstractDebugAction { super(id, label, '', debugService, keybindingService); } - run(session: IDebugSession): Promise { - this.debugService.focusStackFrame(undefined, undefined, session, true); + async run(session: IDebugSession): Promise { + await this.debugService.focusStackFrame(undefined, undefined, session, true); const stackFrame = this.debugService.getViewModel().focusedStackFrame; if (stackFrame) { return stackFrame.openInEditor(this.editorService, true); @@ -380,7 +371,7 @@ export class FocusSessionAction extends AbstractDebugAction { export class CopyValueAction extends Action { static readonly ID = 'workbench.debug.viewlet.action.copyValue'; - static LABEL = nls.localize('copyValue', "Copy Value"); + static readonly LABEL = nls.localize('copyValue', "Copy Value"); constructor( id: string, label: string, private value: any, private context: string, diff --git a/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts b/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts index 68cf65a7931..d102042652b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 3f1ec22ef44..25a45e50d2f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -60,10 +60,10 @@ export const DISCONNECT_LABEL = nls.localize('disconnect', "Disconnect"); export const STOP_LABEL = nls.localize('stop', "Stop"); export const CONTINUE_LABEL = nls.localize('continueDebug', "Continue"); -function getThreadAndRun(accessor: ServicesAccessor, threadId: number | undefined, run: (thread: IThread) => Promise): void { +function getThreadAndRun(accessor: ServicesAccessor, threadId: number | any, run: (thread: IThread) => Promise): void { const debugService = accessor.get(IDebugService); let thread: IThread | undefined; - if (threadId) { + if (typeof threadId === 'number') { debugService.getModel().getSessions().forEach(s => { if (!thread) { thread = s.getThread(threadId); @@ -104,6 +104,10 @@ function getFrame(debugService: IDebugService, frameId: string | undefined): ISt export function registerCommands(): void { + // These commands are used in call stack context menu, call stack inline actions, command pallete, debug toolbar, mac native touch bar + // When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id + // Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command pallete) we do not pass any id and just take whatever is the focussed thread + // Same for stackFrame commands and session commands. CommandsRegistry.registerCommand({ id: COPY_STACK_TRACE_ID, handler: async (accessor: ServicesAccessor, _: string, frameId: string | undefined) => { @@ -119,21 +123,21 @@ export function registerCommands(): void { CommandsRegistry.registerCommand({ id: REVERSE_CONTINUE_ID, - handler: (accessor: ServicesAccessor, threadId: number | undefined) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, thread => thread.reverseContinue()); } }); CommandsRegistry.registerCommand({ id: STEP_BACK_ID, - handler: (accessor: ServicesAccessor, threadId: number | undefined) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, thread => thread.stepBack()); } }); CommandsRegistry.registerCommand({ id: TERMINATE_THREAD_ID, - handler: (accessor: ServicesAccessor, threadId: number | undefined) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, thread => thread.terminate()); } }); @@ -213,7 +217,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.F10, when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), - handler: (accessor: ServicesAccessor, threadId: number) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, (thread: IThread) => thread.next()); } }); @@ -223,7 +227,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging primary: KeyCode.F11, when: CONTEXT_IN_DEBUG_MODE, - handler: (accessor: ServicesAccessor, threadId: number) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, (thread: IThread) => thread.stepIn()); } }); @@ -233,7 +237,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Shift | KeyCode.F11, when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), - handler: (accessor: ServicesAccessor, threadId: number) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, (thread: IThread) => thread.stepOut()); } }); @@ -243,7 +247,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.F6, when: CONTEXT_DEBUG_STATE.isEqualTo('running'), - handler: (accessor: ServicesAccessor, threadId: number) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, thread => thread.pause()); } }); @@ -292,7 +296,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.F5, when: CONTEXT_IN_DEBUG_MODE, - handler: (accessor: ServicesAccessor, threadId: number | undefined) => { + handler: (accessor: ServicesAccessor, threadId: number | any) => { getThreadAndRun(accessor, threadId, thread => thread.continue()); } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 784694a387a..ea6cc952742 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -104,8 +104,8 @@ class LogPointAction extends EditorAction { export class RunToCursorAction extends EditorAction { - public static ID = 'editor.debug.action.runToCursor'; - public static LABEL = nls.localize('runToCursor', "Run to Cursor"); + public static readonly ID = 'editor.debug.action.runToCursor'; + public static readonly LABEL = nls.localize('runToCursor', "Run to Cursor"); constructor() { super({ diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index a395d10adc1..3b7f7be2ce8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as env from 'vs/base/common/platform'; import { visit } from 'vs/base/common/json'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardTokenType } from 'vs/editor/common/modes'; diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 506aafc1781..13f6651d59b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -94,12 +94,12 @@ export class DebugHoverWidget implements IContentWidget { if (colors.editorHoverBackground) { this.domNode.style.backgroundColor = colors.editorHoverBackground.toString(); } else { - this.domNode.style.backgroundColor = null; + this.domNode.style.backgroundColor = ''; } if (colors.editorHoverBorder) { this.domNode.style.border = `1px solid ${colors.editorHoverBorder}`; } else { - this.domNode.style.border = null; + this.domNode.style.border = ''; } })); this.toDispose.push(this.tree.onDidChangeContentHeight(() => this.layoutTreeAndContainer())); @@ -174,7 +174,7 @@ export class DebugHoverWidget implements IContentWidget { return this.doShow(pos, expression, focus); } - private static _HOVER_HIGHLIGHT_DECORATION_OPTIONS = ModelDecorationOptions.register({ + private static readonly _HOVER_HIGHLIGHT_DECORATION_OPTIONS = ModelDecorationOptions.register({ className: 'hoverHighlight' }); diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index ccb8004fccc..6b15aedcf4d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -24,7 +24,6 @@ import * as debugactions from 'vs/workbench/contrib/debug/browser/debugActions'; import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService'; -import { TaskError } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/contrib/files/common/files'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -144,13 +143,13 @@ export class DebugService implements IDebugService { session.configuration.request = 'attach'; session.configuration.port = event.port; session.setSubId(event.subId); - this.launchOrAttachToSession(session).then(undefined, errors.onUnexpectedError); + this.launchOrAttachToSession(session); } })); this.toDispose.push(this.extensionHostDebugService.onTerminateSession(event => { const session = this.model.getSession(event.sessionId); if (session && session.subId === event.subId) { - session.disconnect().then(undefined, errors.onUnexpectedError); + session.disconnect(); } })); this.toDispose.push(this.extensionHostDebugService.onLogToSession(event => { @@ -253,96 +252,99 @@ export class DebugService implements IDebugService { * main entry point * properly manages compounds, checks for errors and handles the initializing state. */ - startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise { + async startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise { this.startInitializingState(); - // make sure to save all files and that the configuration is up to date - return this.extensionService.activateByEvent('onDebug').then(() => { - return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() => { - return this.extensionService.whenInstalledExtensionsRegistered().then(() => { + try { + // make sure to save all files and that the configuration is up to date + await this.extensionService.activateByEvent('onDebug'); + await this.textFileService.saveAll(); + await this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined); + await this.extensionService.whenInstalledExtensionsRegistered(); - let config: IConfig | undefined; - let compound: ICompound | undefined; - if (!configOrName) { - configOrName = this.configurationManager.selectedConfiguration.name; + let config: IConfig | undefined; + let compound: ICompound | undefined; + if (!configOrName) { + configOrName = this.configurationManager.selectedConfiguration.name; + } + if (typeof configOrName === 'string' && launch) { + config = launch.getConfiguration(configOrName); + compound = launch.getCompound(configOrName); + + const sessions = this.model.getSessions(); + const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName); + if (sessions.some(s => s.configuration.name === configOrName && (!launch || !launch.workspace || !s.root || s.root.uri.toString() === launch.workspace.uri.toString()))) { + throw new Error(alreadyRunningMessage); + } + if (compound && compound.configurations && sessions.some(p => compound!.configurations.indexOf(p.configuration.name) !== -1)) { + throw new Error(alreadyRunningMessage); + } + } else if (typeof configOrName !== 'string') { + config = configOrName; + } + + if (compound) { + // we are starting a compound debug, first do some error checking and than start each configuration in the compound + if (!compound.configurations) { + throw new Error(nls.localize({ key: 'compoundMustHaveConfigurations', comment: ['compound indicates a "compounds" configuration item', '"configurations" is an attribute and should not be localized'] }, + "Compound must have \"configurations\" attribute set in order to start multiple configurations.")); + } + + const values = await Promise.all(compound.configurations.map(configData => { + const name = typeof configData === 'string' ? configData : configData.name; + if (name === compound!.name) { + return Promise.resolve(false); } - if (typeof configOrName === 'string' && launch) { - config = launch.getConfiguration(configOrName); - compound = launch.getCompound(configOrName); - const sessions = this.model.getSessions(); - const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName); - if (sessions.some(s => s.configuration.name === configOrName && (!launch || !launch.workspace || !s.root || s.root.uri.toString() === launch.workspace.uri.toString()))) { - return Promise.reject(new Error(alreadyRunningMessage)); + let launchForName: ILaunch | undefined; + if (typeof configData === 'string') { + const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name)); + if (launchesContainingName.length === 1) { + launchForName = launchesContainingName[0]; + } else if (launch && launchesContainingName.length > 1 && launchesContainingName.indexOf(launch) >= 0) { + // If there are multiple launches containing the configuration give priority to the configuration in the current launch + launchForName = launch; + } else { + throw new Error(launchesContainingName.length === 0 ? nls.localize('noConfigurationNameInWorkspace', "Could not find launch configuration '{0}' in the workspace.", name) + : nls.localize('multipleConfigurationNamesInWorkspace', "There are multiple launch configurations '{0}' in the workspace. Use folder name to qualify the configuration.", name)); } - if (compound && compound.configurations && sessions.some(p => compound!.configurations.indexOf(p.configuration.name) !== -1)) { - return Promise.reject(new Error(alreadyRunningMessage)); + } else if (configData.folder) { + const launchesMatchingConfigData = this.configurationManager.getLaunches().filter(l => l.workspace && l.workspace.name === configData.folder && !!l.getConfiguration(configData.name)); + if (launchesMatchingConfigData.length === 1) { + launchForName = launchesMatchingConfigData[0]; + } else { + throw new Error(nls.localize('noFolderWithName', "Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", configData.folder, configData.name, compound!.name)); } - } else if (typeof configOrName !== 'string') { - config = configOrName; } - if (compound) { - // we are starting a compound debug, first do some error checking and than start each configuration in the compound - if (!compound.configurations) { - return Promise.reject(new Error(nls.localize({ key: 'compoundMustHaveConfigurations', comment: ['compound indicates a "compounds" configuration item', '"configurations" is an attribute and should not be localized'] }, - "Compound must have \"configurations\" attribute set in order to start multiple configurations."))); - } + return this.createSession(launchForName, launchForName!.getConfiguration(name), options); + })); - return Promise.all(compound.configurations.map(configData => { - const name = typeof configData === 'string' ? configData : configData.name; - if (name === compound!.name) { - return Promise.resolve(false); - } + const result = values.every(success => !!success); // Compound launch is a success only if each configuration launched successfully + this.endInitializingState(); + return result; + } - let launchForName: ILaunch | undefined; - if (typeof configData === 'string') { - const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name)); - if (launchesContainingName.length === 1) { - launchForName = launchesContainingName[0]; - } else if (launch && launchesContainingName.length > 1 && launchesContainingName.indexOf(launch) >= 0) { - // If there are multiple launches containing the configuration give priority to the configuration in the current launch - launchForName = launch; - } else { - return Promise.reject(new Error(launchesContainingName.length === 0 ? nls.localize('noConfigurationNameInWorkspace', "Could not find launch configuration '{0}' in the workspace.", name) - : nls.localize('multipleConfigurationNamesInWorkspace', "There are multiple launch configurations '{0}' in the workspace. Use folder name to qualify the configuration.", name))); - } - } else if (configData.folder) { - const launchesMatchingConfigData = this.configurationManager.getLaunches().filter(l => l.workspace && l.workspace.name === configData.folder && !!l.getConfiguration(configData.name)); - if (launchesMatchingConfigData.length === 1) { - launchForName = launchesMatchingConfigData[0]; - } else { - return Promise.reject(new Error(nls.localize('noFolderWithName', "Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", configData.folder, configData.name, compound!.name))); - } - } + if (configOrName && !config) { + const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : JSON.stringify(configOrName)) : + nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist."); + throw new Error(message); + } - return this.createSession(launchForName, launchForName!.getConfiguration(name), options); - })).then(values => values.every(success => !!success)); // Compound launch is a success only if each configuration launched successfully - } - - if (configOrName && !config) { - const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : JSON.stringify(configOrName)) : - nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist."); - return Promise.reject(new Error(message)); - } - - return this.createSession(launch, config, options); - }); - })); - }).then(success => { + const result = await this.createSession(launch, config, options); + this.endInitializingState(); + return result; + } catch (err) { // make sure to get out of initializing state, and propagate the result - this.endInitializingState(); - return success; - }, err => { this.endInitializingState(); return Promise.reject(err); - }); + } } /** * gets the debugger for the type, resolves configurations by providers, substitutes variables and runs prelaunch tasks */ - private createSession(launch: ILaunch | undefined, config: IConfig | undefined, options?: IDebugSessionOptions): Promise { + private async createSession(launch: ILaunch | undefined, config: IConfig | undefined, options?: IDebugSessionOptions): Promise { // We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes. // Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config. let type: string | undefined; @@ -358,66 +360,71 @@ export class DebugService implements IDebugService { config!.noDebug = true; } - const debuggerThenable: Promise = type ? Promise.resolve() : this.configurationManager.guessDebugger().then(dbgr => { type = dbgr && dbgr.type; }); - return debuggerThenable.then(() => { - this.initCancellationToken = new CancellationTokenSource(); - return this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config!, this.initCancellationToken.token).then(config => { - // a falsy config indicates an aborted launch - if (config && config.type) { - return this.substituteVariables(launch, config).then(resolvedConfig => { + if (!type) { + const guess = await this.configurationManager.guessDebugger(); + if (guess) { + type = guess.type; + } + } - if (!resolvedConfig) { - // User canceled resolving of interactive variables, silently return - return false; - } + this.initCancellationToken = new CancellationTokenSource(); + const configByProviders = await this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config!, this.initCancellationToken.token); + // a falsy config indicates an aborted launch + if (configByProviders && configByProviders.type) { + try { + const resolvedConfig = await this.substituteVariables(launch, configByProviders); - if (!this.configurationManager.getDebugger(resolvedConfig.type) || (config.request !== 'attach' && config.request !== 'launch')) { - let message: string; - if (config.request !== 'attach' && config.request !== 'launch') { - message = config.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request) - : nls.localize('debugRequesMissing', "Attribute '{0}' is missing from the chosen debug configuration.", 'request'); - - } else { - message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) : - nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."); - } - - return this.showError(message).then(() => false); - } - - const workspace = launch ? launch.workspace : undefined; - return this.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask).then(result => { - if (result === TaskRunResult.Success) { - return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options); - } - return false; - }); - }, err => { - if (err && err.message) { - return this.showError(err.message).then(() => false); - } - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved and that you have a debug extension installed for that file type.")) - .then(() => false); - } - - return !!launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); - }); + if (!resolvedConfig) { + // User canceled resolving of interactive variables, silently return + return false; } - if (launch && type && config === null) { // show launch.json only for "config" being "null". - return launch.openConfigFile(false, true, type, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); + if (!this.configurationManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) { + let message: string; + if (configByProviders.request !== 'attach' && configByProviders.request !== 'launch') { + message = configByProviders.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', configByProviders.request) + : nls.localize('debugRequesMissing', "Attribute '{0}' is missing from the chosen debug configuration.", 'request'); + + } else { + message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) : + nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration."); + } + + await this.showError(message); + return false; + } + + const workspace = launch ? launch.workspace : undefined; + const taskResult = await this.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask); + if (taskResult === TaskRunResult.Success) { + return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options); + } + return false; + } catch (err) { + if (err && err.message) { + await this.showError(err.message); + } else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + await this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved and that you have a debug extension installed for that file type.")); + } + if (launch) { + await launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined); } return false; - }); - }); + } + } + + if (launch && type && configByProviders === null) { // show launch.json only for "config" being "null". + await launch.openConfigFile(false, true, type, this.initCancellationToken ? this.initCancellationToken.token : undefined); + } + + return false; } /** * instantiates the new session, initializes the session, registers session listeners and reports telemetry */ - private doCreateSession(root: IWorkspaceFolder | undefined, configuration: { resolved: IConfig, unresolved: IConfig | undefined }, options?: IDebugSessionOptions): Promise { + private async doCreateSession(root: IWorkspaceFolder | undefined, configuration: { resolved: IConfig, unresolved: IConfig | undefined }, options?: IDebugSessionOptions): Promise { const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model, options); this.model.addSession(session); @@ -431,10 +438,11 @@ export class DebugService implements IDebugService { const openDebug = this.configurationService.getValue('debug').openDebug; // Open debug viewlet based on the visibility of the side bar and openDebug setting. Do not open for 'run without debug' if (!configuration.resolved.noDebug && (openDebug === 'openOnSessionStart' || (openDebug === 'openOnFirstSessionStart' && this.viewModel.firstSessionStart))) { - this.viewletService.openViewlet(VIEWLET_ID).then(undefined, errors.onUnexpectedError); + await this.viewletService.openViewlet(VIEWLET_ID); } - return this.launchOrAttachToSession(session).then(() => { + try { + await this.launchOrAttachToSession(session); const internalConsoleOptions = session.configuration.internalConsoleOptions || this.configurationService.getValue('debug').internalConsoleOptions; if (internalConsoleOptions === 'openOnSessionStart' || (this.viewModel.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) { @@ -452,12 +460,14 @@ export class DebugService implements IDebugService { // since the initialized response has arrived announce the new Session (including extensions) this._onDidNewSession.fire(session); - return this.telemetryDebugSessionStart(root, session.configuration.type); - }).then(() => true, (error: Error | string) => { + await this.telemetryDebugSessionStart(root, session.configuration.type); + + return true; + } catch (error) { if (errors.isPromiseCanceledError(error)) { // don't show 'canceled' error messages to the user #7906 - return Promise.resolve(false); + return false; } // Show the repl if some error got logged there #5870 @@ -467,27 +477,29 @@ export class DebugService implements IDebugService { if (session.configuration && session.configuration.request === 'attach' && session.configuration.__autoAttach) { // ignore attach timeouts in auto attach mode - return Promise.resolve(false); + return false; } const errorMessage = error instanceof Error ? error.message : error; this.telemetryDebugMisconfiguration(session.configuration ? session.configuration.type : undefined, errorMessage); - return this.showError(errorMessage, isErrorWithActions(error) ? error.actions : []).then(() => false); - }); + + await this.showError(errorMessage, isErrorWithActions(error) ? error.actions : []); + return false; + } } - private launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise { + private async launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise { const dbgr = this.configurationManager.getDebugger(session.configuration.type); - return session.initialize(dbgr!).then(() => { - return session.launchOrAttach(session.configuration).then(() => { - if (forceFocus || !this.viewModel.focusedSession) { - this.focusStackFrame(undefined, undefined, session); - } - }); - }).then(undefined, err => { + try { + await session.initialize(dbgr!); + await session.launchOrAttach(session.configuration); + if (forceFocus || !this.viewModel.focusedSession) { + await this.focusStackFrame(undefined, undefined, session); + } + } catch (err) { session.shutdown(); return Promise.reject(err); - }); + } } private registerSessionListeners(session: IDebugSession): void { @@ -506,7 +518,7 @@ export class DebugService implements IDebugService { } })); - this.toDispose.push(session.onDidEndAdapter(adapterExitEvent => { + this.toDispose.push(session.onDidEndAdapter(async adapterExitEvent => { if (adapterExitEvent.error) { this.notificationService.error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly ({0})", adapterExitEvent.error.message || adapterExitEvent.error.toString())); @@ -520,9 +532,11 @@ export class DebugService implements IDebugService { this.telemetryDebugSessionStop(session, adapterExitEvent); if (session.configuration.postDebugTask) { - this.runTask(session.root, session.configuration.postDebugTask).then(undefined, err => - this.notificationService.error(err) - ); + try { + await this.runTask(session.root, session.configuration.postDebugTask); + } catch (err) { + this.notificationService.error(err); + } } session.shutdown(); this.endInitializingState(); @@ -530,7 +544,7 @@ export class DebugService implements IDebugService { const focusedSession = this.viewModel.focusedSession; if (focusedSession && focusedSession.getId() === session.getId()) { - this.focusStackFrame(undefined); + await this.focusStackFrame(undefined); } if (this.model.getSessions().length === 0) { @@ -548,87 +562,93 @@ export class DebugService implements IDebugService { })); } - restartSession(session: IDebugSession, restartData?: any): Promise { - return this.textFileService.saveAll().then(() => { - const isAutoRestart = !!restartData; - const runTasks: () => Promise = () => { - if (isAutoRestart) { - // Do not run preLaunch and postDebug tasks for automatic restarts - return Promise.resolve(TaskRunResult.Success); + async restartSession(session: IDebugSession, restartData?: any): Promise { + await this.textFileService.saveAll(); + const isAutoRestart = !!restartData; + + const runTasks: () => Promise = async () => { + if (isAutoRestart) { + // Do not run preLaunch and postDebug tasks for automatic restarts + return Promise.resolve(TaskRunResult.Success); + } + + await this.runTask(session.root, session.configuration.postDebugTask); + return this.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask); + }; + + if (session.capabilities.supportsRestartRequest) { + const taskResult = await runTasks(); + if (taskResult === TaskRunResult.Success) { + await session.restart(); + } + + return; + } + + if (isExtensionHostDebugging(session.configuration)) { + const taskResult = await runTasks(); + if (taskResult === TaskRunResult.Success) { + this.extensionHostDebugService.reload(session.getId()); + } + + return; + } + + const shouldFocus = !!this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId(); + // If the restart is automatic -> disconnect, otherwise -> terminate #55064 + if (isAutoRestart) { + await session.disconnect(true); + } else { + await session.terminate(true); + } + + return new Promise((c, e) => { + setTimeout(async () => { + const taskResult = await runTasks(); + if (taskResult !== TaskRunResult.Success) { + return; } - return this.runTask(session.root, session.configuration.postDebugTask) - .then(() => this.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask)); - }; + // Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration + let needsToSubstitute = false; + let unresolved: IConfig | undefined; + const launch = session.root ? this.configurationManager.getLaunch(session.root.uri) : undefined; + if (launch) { + unresolved = launch.getConfiguration(session.configuration.name); + if (unresolved && !equals(unresolved, session.unresolvedConfiguration)) { + // Take the type from the session since the debug extension might overwrite it #21316 + unresolved.type = session.configuration.type; + unresolved.noDebug = session.configuration.noDebug; + needsToSubstitute = true; + } + } - if (session.capabilities.supportsRestartRequest) { - return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? session.restart() : undefined); - } + let resolved: IConfig | undefined | null = session.configuration; + if (launch && needsToSubstitute && unresolved) { + this.initCancellationToken = new CancellationTokenSource(); + const resolvedByProviders = await this.configurationManager.resolveConfigurationByProviders(launch.workspace ? launch.workspace.uri : undefined, unresolved.type, unresolved, this.initCancellationToken.token); + if (resolvedByProviders) { + resolved = await this.substituteVariables(launch, resolvedByProviders); + } else { + resolved = resolvedByProviders; + } + } - if (isExtensionHostDebugging(session.configuration)) { - return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.getId()) : undefined); - } + if (!resolved) { + return c(undefined); + } - const shouldFocus = !!this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId(); - // If the restart is automatic -> disconnect, otherwise -> terminate #55064 - return (isAutoRestart ? session.disconnect(true) : session.terminate(true)).then(() => { + session.setConfiguration({ resolved, unresolved }); + session.configuration.__restart = restartData; - return new Promise((c, e) => { - setTimeout(() => { - runTasks().then(taskResult => { - if (taskResult !== TaskRunResult.Success) { - return; - } - - // Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration - let needsToSubstitute = false; - let unresolved: IConfig | undefined; - const launch = session.root ? this.configurationManager.getLaunch(session.root.uri) : undefined; - if (launch) { - unresolved = launch.getConfiguration(session.configuration.name); - if (unresolved && !equals(unresolved, session.unresolvedConfiguration)) { - // Take the type from the session since the debug extension might overwrite it #21316 - unresolved.type = session.configuration.type; - unresolved.noDebug = session.configuration.noDebug; - needsToSubstitute = true; - } - } - - let substitutionThenable: Promise = Promise.resolve(session.configuration); - if (launch && needsToSubstitute && unresolved) { - this.initCancellationToken = new CancellationTokenSource(); - substitutionThenable = this.configurationManager.resolveConfigurationByProviders(launch.workspace ? launch.workspace.uri : undefined, unresolved.type, unresolved, this.initCancellationToken.token) - .then(resolved => { - if (resolved) { - // start debugging - return this.substituteVariables(launch, resolved); - } else if (resolved === null) { - // abort debugging silently and open launch.json - return Promise.resolve(null); - } else { - // abort debugging silently - return Promise.resolve(undefined); - } - }); - } - substitutionThenable.then(resolved => { - - if (!resolved) { - return c(undefined); - } - - session.setConfiguration({ resolved, unresolved }); - session.configuration.__restart = restartData; - - this.launchOrAttachToSession(session, shouldFocus).then(() => { - this._onDidNewSession.fire(session); - c(undefined); - }, err => e(err)); - }); - }); - }, 300); - }); - }); + try { + await this.launchOrAttachToSession(session, shouldFocus); + this._onDidNewSession.fire(session); + c(undefined); + } catch (error) { + e(error); + } + }, 300); }); } @@ -646,7 +666,7 @@ export class DebugService implements IDebugService { return Promise.all(sessions.map(s => s.terminate())); } - private substituteVariables(launch: ILaunch | undefined, config: IConfig): Promise { + private async substituteVariables(launch: ILaunch | undefined, config: IConfig): Promise { const dbg = this.configurationManager.getDebugger(config.type); if (dbg) { let folder: IWorkspaceFolder | undefined = undefined; @@ -658,12 +678,12 @@ export class DebugService implements IDebugService { folder = folders[0]; } } - return dbg.substituteVariables(folder, config).then(config => { - return config; - }, (err: Error) => { + try { + return await dbg.substituteVariables(folder, config); + } catch (err) { this.showError(err.message); return undefined; // bail out - }); + } } return Promise.resolve(config); } @@ -681,9 +701,9 @@ export class DebugService implements IDebugService { //---- task management - private runTaskAndCheckErrors(root: IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise { - - return this.runTask(root, taskId).then((taskSummary: ITaskSummary) => { + private async runTaskAndCheckErrors(root: IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise { + try { + const taskSummary = await this.runTask(root, taskId); const errorCount = taskId ? this.markerService.getStatistics().errors : 0; const successExitCode = taskSummary && taskSummary.exitCode === 0; @@ -702,34 +722,35 @@ export class DebugService implements IDebugService { ? nls.localize('preLaunchTaskErrors', "Errors exist after running preLaunchTask '{0}'.", taskLabel) : errorCount === 1 ? nls.localize('preLaunchTaskError', "Error exists after running preLaunchTask '{0}'.", taskLabel) - : nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary.exitCode); + : nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary ? taskSummary.exitCode : 0); - return this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], { + const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], { checkbox: { label: nls.localize('remember', "Remember my choice in user settings"), }, cancelId: 2 - }).then(result => { - if (result.choice === 2) { - return Promise.resolve(TaskRunResult.Failure); - } - const debugAnyway = result.choice === 0; - if (result.checkboxChecked) { - this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors'); - } - if (debugAnyway) { - return TaskRunResult.Success; - } - - this.panelService.openPanel(Constants.MARKERS_PANEL_ID); - return Promise.resolve(TaskRunResult.Failure); }); - }, (err: TaskError) => { - return this.showError(err.message, [this.taskService.configureAction()]); - }); + + if (result.choice === 2) { + return Promise.resolve(TaskRunResult.Failure); + } + const debugAnyway = result.choice === 0; + if (result.checkboxChecked) { + this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors'); + } + if (debugAnyway) { + return TaskRunResult.Success; + } + + this.panelService.openPanel(Constants.MARKERS_PANEL_ID); + return Promise.resolve(TaskRunResult.Failure); + } catch (err) { + await this.showError(err.message, [this.taskService.configureAction()]); + return TaskRunResult.Failure; + } } - private runTask(root: IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise { + private async runTask(root: IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise { if (!taskId) { return Promise.resolve(null); } @@ -737,58 +758,72 @@ export class DebugService implements IDebugService { return Promise.reject(new Error(nls.localize('invalidTaskReference', "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", typeof taskId === 'string' ? taskId : taskId.type))); } // run a task before starting a debug session - return this.taskService.getTask(root, taskId).then(task => { - if (!task) { - const errorMessage = typeof taskId === 'string' - ? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId) - : nls.localize('DebugTaskNotFound', "Could not find the specified task."); - return Promise.reject(createErrorWithActions(errorMessage)); + const task = await this.taskService.getTask(root, taskId); + if (!task) { + const errorMessage = typeof taskId === 'string' + ? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId) + : nls.localize('DebugTaskNotFound', "Could not find the specified task."); + return Promise.reject(createErrorWithActions(errorMessage)); + } + + // If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340 + let taskStarted = false; + const inactivePromise: Promise = new Promise((c, e) => once(e => { + // When a task isBackground it will go inactive when it is safe to launch. + // But when a background task is terminated by the user, it will also fire an inactive event. + // This means that we will not get to see the real exit code from running the task (undefined when terminated by the user). + // Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this. + return (e.kind === TaskEventKind.Inactive + || (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined)) + && e.taskId === task._id; + }, this.taskService.onDidStateChange)(e => { + taskStarted = true; + c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null); + })); + + const promise: Promise = this.taskService.getActiveTasks().then(async (tasks): Promise => { + if (tasks.filter(t => t._id === task._id).length) { + // Check that the task isn't busy and if it is, wait for it + const busyTasks = await this.taskService.getBusyTasks(); + if (busyTasks.filter(t => t._id === task._id).length) { + return inactivePromise; + } + // task is already running and isn't busy - nothing to do. + return Promise.resolve(null); + } + once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => { + // Task is active, so everything seems to be fine, no need to prompt after 10 seconds + // Use case being a slow running task should not be prompted even though it takes more than 10 seconds + taskStarted = true; + }); + const taskPromise = this.taskService.run(task); + if (task.configurationProperties.isBackground) { + return inactivePromise; } - // If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340 - let taskStarted = false; - const promise: Promise = this.taskService.getActiveTasks().then(tasks => { - if (tasks.filter(t => t._id === task._id).length) { - // task is already running - nothing to do. - return Promise.resolve(null); + return taskPromise; + }); + + return new Promise((c, e) => { + promise.then(result => { + taskStarted = true; + c(result); + }, error => e(error)); + + setTimeout(() => { + if (!taskStarted) { + const errorMessage = typeof taskId === 'string' + ? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.") + : nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId)); + e({ severity: severity.Error, message: errorMessage }); } - once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => { - // Task is active, so everything seems to be fine, no need to prompt after 10 seconds - // Use case being a slow running task should not be prompted even though it takes more than 10 seconds - taskStarted = true; - }); - const taskPromise = this.taskService.run(task); - if (task.configurationProperties.isBackground) { - return new Promise((c, e) => once(e => e.kind === TaskEventKind.Inactive && e.taskId === task._id, this.taskService.onDidStateChange)(() => { - taskStarted = true; - c(null); - })); - } - - return taskPromise; - }); - - return new Promise((c, e) => { - promise.then(result => { - taskStarted = true; - c(result); - }, error => e(error)); - - setTimeout(() => { - if (!taskStarted) { - const errorMessage = typeof taskId === 'string' - ? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.") - : nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId)); - e({ severity: severity.Error, message: errorMessage }); - } - }, 10000); - }); + }, 10000); }); } //---- focus management - focusStackFrame(stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): void { + async focusStackFrame(stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise { if (!session) { if (stackFrame || thread) { session = stackFrame ? stackFrame.thread.session : thread!.session; @@ -817,18 +852,17 @@ export class DebugService implements IDebugService { } if (stackFrame) { - stackFrame.openInEditor(this.editorService, true).then(editor => { - if (editor) { - const control = editor.getControl(); - if (stackFrame && isCodeEditor(control) && control.hasModel()) { - const model = control.getModel(); - if (stackFrame.range.startLineNumber <= model.getLineCount()) { - const lineContent = control.getModel().getLineContent(stackFrame.range.startLineNumber); - aria.alert(nls.localize('debuggingPaused', "Debugging paused {0}, {1} {2} {3}", thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, lineContent)); - } + const editor = await stackFrame.openInEditor(this.editorService, true); + if (editor) { + const control = editor.getControl(); + if (stackFrame && isCodeEditor(control) && control.hasModel()) { + const model = control.getModel(); + if (stackFrame.range.startLineNumber <= model.getLineCount()) { + const lineContent = control.getModel().getLineContent(stackFrame.range.startLineNumber); + aria.alert(nls.localize('debuggingPaused', "Debugging paused {0}, {1} {2} {3}", thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, lineContent)); } } - }); + } } if (session) { this.debugType.set(session.configuration.type); @@ -949,12 +983,12 @@ export class DebugService implements IDebugService { this.storeBreakpoints(); } - sendAllBreakpoints(session?: IDebugSession): Promise { - return Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))) - .then(() => this.sendFunctionBreakpoints(session)) - // send exception breakpoints at the end since some debug adapters rely on the order - .then(() => this.sendExceptionBreakpoints(session)) - .then(() => this.sendDataBreakpoints(session)); + async sendAllBreakpoints(session?: IDebugSession): Promise { + await Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))); + await this.sendFunctionBreakpoints(session); + await this.sendDataBreakpoints(session); + // send exception breakpoints at the end since some debug adapters rely on the order + await this.sendExceptionBreakpoints(session); } private sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { @@ -989,11 +1023,12 @@ export class DebugService implements IDebugService { }); } - private sendToOneOrAllSessions(session: IDebugSession | undefined, send: (session: IDebugSession) => Promise): Promise { + private async sendToOneOrAllSessions(session: IDebugSession | undefined, send: (session: IDebugSession) => Promise): Promise { if (session) { - return send(session); + await send(session); + } else { + await Promise.all(this.model.getSessions().map(s => send(s))); } - return Promise.all(this.model.getSessions().map(s => send(s))).then(() => undefined); } private onFileChanges(fileChangesEvent: FileChangesEvent): void { diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index b368e7026f8..bf8a93ef6de 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -65,7 +65,7 @@ export class DebugSession implements IDebugSession { constructor( private _configuration: { resolved: IConfig, unresolved: IConfig | undefined }, - public root: IWorkspaceFolder, + public root: IWorkspaceFolder | undefined, private model: DebugModel, options: IDebugSessionOptions | undefined, @IDebugService private readonly debugService: IDebugService, @@ -381,6 +381,10 @@ export class DebugSession implements IDebugSession { if (this.raw) { const source = this.getRawSource(uri); const response = await this.raw.breakpointLocations({ source, line: lineNumber }); + if (!response.body || !response.body.breakpoints) { + return []; + } + const positions = response.body.breakpoints.map(bp => ({ lineNumber: bp.line, column: bp.column || 1 })); return distinct(positions, p => `${p.lineNumber}:${p.column}`); @@ -723,9 +727,9 @@ export class DebugSession implements IDebugSession { // Call fetch call stack twice, the first only return the top stack frame. // Second retrieves the rest of the call stack. For performance reasons #25605 const promises = this.model.fetchCallStack(thread); - const focus = () => { + const focus = async () => { if (!event.body.preserveFocusHint && thread.getCallStack().length) { - this.debugService.focusStackFrame(undefined, thread); + await this.debugService.focusStackFrame(undefined, thread); if (thread.stoppedDetails) { if (this.configurationService.getValue('debug').openDebug === 'openOnDebugBreak') { this.viewletService.openViewlet(VIEWLET_ID); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index ba384a37df3..f20ab5d427a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -192,10 +192,10 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { super.updateStyles(); if (this.$el) { - this.$el.style.backgroundColor = this.getColor(debugToolBarBackground); + this.$el.style.backgroundColor = this.getColor(debugToolBarBackground) || ''; const widgetShadowColor = this.getColor(widgetShadow); - this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null; + this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : ''; const contrastBorderColor = this.getColor(contrastBorder); const borderColor = this.getColor(debugToolBarBorder); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index 10681dbeda0..01d0a2e96d2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -214,7 +214,7 @@ export class DebugViewlet extends ViewContainerViewlet { class ToggleReplAction extends TogglePanelAction { static readonly ID = 'debug.toggleRepl'; - static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console'); + static readonly LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console'); constructor(id: string, label: string, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index da8e4c728e8..a72473eef46 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -240,7 +240,7 @@ class RootTreeItem extends BaseTreeItem { class SessionTreeItem extends BaseTreeItem { - private static URL_REGEXP = /^(https?:\/\/[^/]+)(\/.*)$/; + private static readonly URL_REGEXP = /^(https?:\/\/[^/]+)(\/.*)$/; private _session: IDebugSession; private _initialized: boolean; diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-function-disabled.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-function-disabled.svg old mode 100755 new mode 100644 diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-function-unverified.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-function-unverified.svg old mode 100755 new mode 100644 diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-function.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-function.svg old mode 100755 new mode 100644 diff --git a/src/vs/workbench/contrib/debug/browser/media/current-and-breakpoint.svg b/src/vs/workbench/contrib/debug/browser/media/current-and-breakpoint.svg old mode 100755 new mode 100644 diff --git a/src/vs/workbench/contrib/debug/browser/media/current-arrow.svg b/src/vs/workbench/contrib/debug/browser/media/current-arrow.svg old mode 100755 new mode 100644 diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index 7bb29ce694b..e8f8bad37cd 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -50,7 +50,7 @@ .monaco-editor .debug-breakpoint-placeholder::before, .monaco-editor .debug-top-stack-frame-column::before { content: " "; - width: 1.3em; + width: 0.9em; display: inline-block; vertical-align: text-bottom; margin-right: 2px; @@ -62,9 +62,6 @@ } .monaco-editor .inline-breakpoint-widget { - width: 1.3em; - height: 1.3em; - margin-left: 0.61em; cursor: pointer; } @@ -178,7 +175,7 @@ /* White color when element is selected and list is focused. White looks better on blue selection background. */ .monaco-workbench .monaco-list:focus .monaco-list-row.selected .expression .name, .monaco-workbench .monaco-list:focus .monaco-list-row.selected .expression .value { - color: white; + color: inherit; } .monaco-workbench .monaco-list-row .expression .name { diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 2ed61ccf6b0..f49d190d4b0 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -445,7 +445,8 @@ background: url('add-dark.svg') center center no-repeat; } -.hc-black .debug-viewlet .debug-action.add-watch-expression { +.hc-black .debug-viewlet .debug-action.add-watch-expression, +.hc-black .debug-viewlet .debug-action.add-function-breakpoint { background: url('add-hc.svg') center center no-repeat; } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 3935ae45643..76d330bdc5c 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -919,7 +919,7 @@ class AcceptReplInputAction extends EditorAction { } run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { - SuggestController.get(editor).acceptSelectedSuggestion(); + SuggestController.get(editor).acceptSelectedSuggestion(false, true); accessor.get(IPrivateReplService).acceptReplInput(); } } @@ -941,7 +941,7 @@ class FilterReplAction extends EditorAction { } run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { - SuggestController.get(editor).acceptSelectedSuggestion(); + SuggestController.get(editor).acceptSelectedSuggestion(false, true); accessor.get(IPrivateReplService).focusRepl(); } } @@ -984,7 +984,7 @@ class SelectReplActionViewItem extends FocusSessionActionViewItem { class SelectReplAction extends Action { static readonly ID = 'workbench.action.debug.selectRepl'; - static LABEL = nls.localize('selectRepl', "Select Debug Console"); + static readonly LABEL = nls.localize('selectRepl', "Select Debug Console"); constructor(id: string, label: string, @IDebugService private readonly debugService: IDebugService, @@ -993,10 +993,10 @@ class SelectReplAction extends Action { super(id, label); } - run(session: IDebugSession): Promise { + async run(session: IDebugSession): Promise { // If session is already the focused session we need to manualy update the tree since view model will not send a focused change event if (session && session.state !== State.Inactive && session !== this.debugService.getViewModel().focusedSession) { - this.debugService.focusStackFrame(undefined, undefined, session, true); + await this.debugService.focusStackFrame(undefined, undefined, session, true); } else { this.replService.selectSession(session); } @@ -1007,7 +1007,7 @@ class SelectReplAction extends Action { export class ClearReplAction extends Action { static readonly ID = 'workbench.debug.panel.action.clearReplAction'; - static LABEL = nls.localize('clearRepl', "Clear Console"); + static readonly LABEL = nls.localize('clearRepl', "Clear Console"); constructor(id: string, label: string, @IPanelService private readonly panelService: IPanelService diff --git a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts index 6078564168b..866505f7a91 100644 --- a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts +++ b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts @@ -12,6 +12,7 @@ import { IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme'; import { addClass, removeClass, createStyleSheet } from 'vs/base/browser/dom'; +import { assertIsDefined } from 'vs/base/common/types'; // colors for theming @@ -56,7 +57,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri protected updateStyles(): void { super.updateStyles(); - const container = this.layoutService.getContainer(Parts.STATUSBAR_PART); + const container = assertIsDefined(this.layoutService.getContainer(Parts.STATUSBAR_PART)); if (isStatusbarInDebugMode(this.debugService)) { addClass(container, 'debugging'); } else { @@ -65,7 +66,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri // Container Colors const backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND)); - container.style.backgroundColor = backgroundColor; + container.style.backgroundColor = backgroundColor || ''; container.style.color = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_DEBUGGING_FOREGROUND, STATUS_BAR_FOREGROUND)); // Border Color diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 56483de1cb2..f7fb8d0f9eb 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -100,8 +100,10 @@ export class VariablesView extends ViewletPanel { CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService); - const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer'); - this.toolbar.setActions([collapseAction])(); + if (this.toolbar) { + const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer'); + this.toolbar.setActions([collapseAction])(); + } this.tree.updateChildren(); this._register(this.debugService.getViewModel().onDidFocusStackFrame(sf => { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index b48786ed88b..db51214714d 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -74,10 +74,12 @@ export class WatchExpressionsView extends ViewletPanel { this.tree.setInput(this.debugService).then(undefined, onUnexpectedError); CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService); - const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService); - const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer'); - const removeAllWatchExpressionsAction = new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService); - this.toolbar.setActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction])(); + if (this.toolbar) { + const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService); + const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer'); + const removeAllWatchExpressionsAction = new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService); + this.toolbar.setActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction])(); + } this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 81e4d032dba..5438069c05a 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -160,7 +160,7 @@ export interface IDebugSession extends ITreeElement { readonly configuration: IConfig; readonly unresolvedConfiguration: IConfig | undefined; readonly state: State; - readonly root: IWorkspaceFolder; + readonly root: IWorkspaceFolder | undefined; readonly parentSession: IDebugSession | undefined; readonly subId: string | undefined; @@ -732,7 +732,7 @@ export interface IDebugService { /** * Sets the focused stack frame and evaluates all expressions against the newly focused stack frame, */ - focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): void; + focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise; /** * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes. diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index ec8d05b8078..489315df4b7 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -27,7 +27,7 @@ import { mixin } from 'vs/base/common/objects'; export class ExpressionContainer implements IExpressionContainer { - public static allValues = new Map(); + public static readonly allValues = new Map(); // Use chunks to support variable paging #9537 private static readonly BASE_CHUNK_SIZE = 100; @@ -172,7 +172,7 @@ export class ExpressionContainer implements IExpressionContainer { } export class Expression extends ExpressionContainer implements IExpression { - static DEFAULT_VALUE = nls.localize('notAvailable', "not available"); + static readonly DEFAULT_VALUE = nls.localize('notAvailable', "not available"); public available: boolean; @@ -617,8 +617,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { } get column(): number | undefined { - // Only respect the column if the user explictly set the column to have an inline breakpoint - return this.verified && this.data && typeof this.data.column === 'number' && typeof this._column === 'number' ? this.data.column : this._column; + return this.verified && this.data && typeof this.data.column === 'number' ? this.data.column : this._column; } get message(): string | undefined { diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 8dbe2680204..3af63c8db6e 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -196,6 +196,9 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA: case 'setBreakpoints': fixSourcePath(true, (request.arguments).source); break; + case 'breakpointLocations': + fixSourcePath(true, (request.arguments).source); + break; case 'source': fixSourcePath(true, (request.arguments).source); break; diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 84ec75a9c84..617c0fa74d3 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -178,7 +178,6 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { if (options.env) { env = objects.mixin(env, options.env); } - delete env.VSCODE_PREVENT_FOREIGN_INSPECT; if (command === 'node') { if (Array.isArray(args) && args.length > 0) { diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 9ce2b7f792e..9178e857417 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -40,7 +40,8 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } - public focusStackFrame(focusedStackFrame: IStackFrame): void { + public focusStackFrame(focusedStackFrame: IStackFrame): Promise { + throw new Error('not implemented'); } sendAllBreakpoints(session?: IDebugSession): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index d8be403fe9e..3f53b905b71 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -143,8 +143,8 @@ export abstract class ExtensionAction extends Action implements IExtensionContai export class InstallAction extends ExtensionAction { - private static INSTALL_LABEL = localize('install', "Install"); - private static INSTALLING_LABEL = localize('installing', "Installing"); + private static readonly INSTALL_LABEL = localize('install', "Install"); + private static readonly INSTALLING_LABEL = localize('installing', "Installing"); private static readonly Class = 'extension-action prominent install'; private static readonly InstallingClass = 'extension-action install installing'; @@ -277,8 +277,8 @@ export class InstallAction extends ExtensionAction { export abstract class InstallInOtherServerAction extends ExtensionAction { - protected static INSTALL_LABEL = localize('install', "Install"); - protected static INSTALLING_LABEL = localize('installing', "Installing"); + protected static readonly INSTALL_LABEL = localize('install', "Install"); + protected static readonly INSTALLING_LABEL = localize('installing', "Installing"); private static readonly Class = 'extension-action prominent install'; private static readonly InstallingClass = 'extension-action install installing'; @@ -566,7 +566,7 @@ export class ExtensionActionViewItem extends ActionViewItem { updateEnabled(): void { super.updateEnabled(); - if (this.options.tabOnlyOnFocus && this.getAction().enabled && !this._hasFocus) { + if (this.label && this.options.tabOnlyOnFocus && this.getAction().enabled && !this._hasFocus) { DOM.removeTabIndexAndUpdateFocus(this.label); } } @@ -577,7 +577,7 @@ export class ExtensionActionViewItem extends ActionViewItem { return; } this._hasFocus = value; - if (this.getAction().enabled) { + if (this.label && this.getAction().enabled) { if (this._hasFocus) { this.label.tabIndex = 0; } else { @@ -726,7 +726,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction { export class InstallAnotherVersionAction extends ExtensionAction { static readonly ID = 'workbench.extensions.action.install.anotherVersion'; - static LABEL = localize('install another version', "Install Another Version..."); + static readonly LABEL = localize('install another version', "Install Another Version..."); constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -832,7 +832,7 @@ export class ExtensionSettingsAction extends ExtensionAction { export class EnableForWorkspaceAction extends ExtensionAction { static readonly ID = 'extensions.enableForWorkspace'; - static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)"); + static readonly LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)"); constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -859,7 +859,7 @@ export class EnableForWorkspaceAction extends ExtensionAction { export class EnableGloballyAction extends ExtensionAction { static readonly ID = 'extensions.enableGlobally'; - static LABEL = localize('enableGloballyAction', "Enable"); + static readonly LABEL = localize('enableGloballyAction', "Enable"); constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -886,7 +886,7 @@ export class EnableGloballyAction extends ExtensionAction { export class DisableForWorkspaceAction extends ExtensionAction { static readonly ID = 'extensions.disableForWorkspace'; - static LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)"); + static readonly LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)"); constructor(readonly runningExtensions: IExtensionDescription[], @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @@ -914,7 +914,7 @@ export class DisableForWorkspaceAction extends ExtensionAction { export class DisableGloballyAction extends ExtensionAction { static readonly ID = 'extensions.disableGlobally'; - static LABEL = localize('disableGloballyAction', "Disable"); + static readonly LABEL = localize('disableGloballyAction', "Disable"); constructor(readonly runningExtensions: IExtensionDescription[], @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -1010,7 +1010,7 @@ export class DisableDropDownAction extends ExtensionEditorDropDownAction { export class CheckForUpdatesAction extends Action { static readonly ID = 'workbench.extensions.action.checkForUpdates'; - static LABEL = localize('checkForUpdates', "Check for Extension Updates"); + static readonly LABEL = localize('checkForUpdates', "Check for Extension Updates"); constructor( id = CheckForUpdatesAction.ID, @@ -1082,7 +1082,7 @@ export class ToggleAutoUpdateAction extends Action { export class EnableAutoUpdateAction extends ToggleAutoUpdateAction { static readonly ID = 'workbench.extensions.action.enableAutoUpdate'; - static LABEL = localize('enableAutoUpdate', "Enable Auto Updating Extensions"); + static readonly LABEL = localize('enableAutoUpdate', "Enable Auto Updating Extensions"); constructor( id = EnableAutoUpdateAction.ID, @@ -1096,7 +1096,7 @@ export class EnableAutoUpdateAction extends ToggleAutoUpdateAction { export class DisableAutoUpdateAction extends ToggleAutoUpdateAction { static readonly ID = 'workbench.extensions.action.disableAutoUpdate'; - static LABEL = localize('disableAutoUpdate', "Disable Auto Updating Extensions"); + static readonly LABEL = localize('disableAutoUpdate', "Disable Auto Updating Extensions"); constructor( id = EnableAutoUpdateAction.ID, @@ -1110,7 +1110,7 @@ export class DisableAutoUpdateAction extends ToggleAutoUpdateAction { export class UpdateAllAction extends Action { static readonly ID = 'workbench.extensions.action.updateAllExtensions'; - static LABEL = localize('updateAll', "Update All Extensions"); + static readonly LABEL = localize('updateAll', "Update All Extensions"); constructor( id = UpdateAllAction.ID, @@ -1417,7 +1417,7 @@ export class InstallExtensionsAction extends OpenExtensionsViewletAction { export class ShowEnabledExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showEnabledExtensions'; - static LABEL = localize('showEnabledExtensions', "Show Enabled Extensions"); + static readonly LABEL = localize('showEnabledExtensions', "Show Enabled Extensions"); constructor( id: string, @@ -1440,7 +1440,7 @@ export class ShowEnabledExtensionsAction extends Action { export class ShowInstalledExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showInstalledExtensions'; - static LABEL = localize('showInstalledExtensions', "Show Installed Extensions"); + static readonly LABEL = localize('showInstalledExtensions', "Show Installed Extensions"); constructor( id: string, @@ -1463,7 +1463,7 @@ export class ShowInstalledExtensionsAction extends Action { export class ShowDisabledExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showDisabledExtensions'; - static LABEL = localize('showDisabledExtensions', "Show Disabled Extensions"); + static readonly LABEL = localize('showDisabledExtensions', "Show Disabled Extensions"); constructor( id: string, @@ -1486,7 +1486,7 @@ export class ShowDisabledExtensionsAction extends Action { export class ClearExtensionsInputAction extends Action { static readonly ID = 'workbench.extensions.action.clearExtensionsInput'; - static LABEL = localize('clearExtensionsInput', "Clear Extensions Input"); + static readonly LABEL = localize('clearExtensionsInput', "Clear Extensions Input"); constructor( id: string, @@ -1517,7 +1517,7 @@ export class ClearExtensionsInputAction extends Action { export class ShowBuiltInExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.listBuiltInExtensions'; - static LABEL = localize('showBuiltInExtensions', "Show Built-in Extensions"); + static readonly LABEL = localize('showBuiltInExtensions', "Show Built-in Extensions"); constructor( id: string, @@ -1540,7 +1540,7 @@ export class ShowBuiltInExtensionsAction extends Action { export class ShowOutdatedExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.listOutdatedExtensions'; - static LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions"); + static readonly LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions"); constructor( id: string, @@ -1563,7 +1563,7 @@ export class ShowOutdatedExtensionsAction extends Action { export class ShowPopularExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showPopularExtensions'; - static LABEL = localize('showPopularExtensions', "Show Popular Extensions"); + static readonly LABEL = localize('showPopularExtensions', "Show Popular Extensions"); constructor( id: string, @@ -1586,7 +1586,7 @@ export class ShowPopularExtensionsAction extends Action { export class ShowRecommendedExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showRecommendedExtensions'; - static LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions"); + static readonly LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions"); constructor( id: string, @@ -1609,7 +1609,7 @@ export class ShowRecommendedExtensionsAction extends Action { export class InstallWorkspaceRecommendedExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.installWorkspaceRecommendedExtensions'; - static LABEL = localize('installWorkspaceRecommendedExtensions', "Install All Workspace Recommended Extensions"); + static readonly LABEL = localize('installWorkspaceRecommendedExtensions', "Install All Workspace Recommended Extensions"); private _recommendations: IExtensionRecommendation[] = []; get recommendations(): IExtensionRecommendation[] { return this._recommendations; } @@ -1674,7 +1674,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { export class InstallRecommendedExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.installRecommendedExtension'; - static LABEL = localize('installRecommendedExtension', "Install Recommended Extension"); + static readonly LABEL = localize('installRecommendedExtension', "Install Recommended Extension"); private extensionId: string; @@ -1765,7 +1765,7 @@ export class UndoIgnoreExtensionRecommendationAction extends Action { export class ShowRecommendedKeymapExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showRecommendedKeymapExtensions'; - static SHORT_LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps"); + static readonly SHORT_LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps"); constructor( id: string, @@ -1788,7 +1788,7 @@ export class ShowRecommendedKeymapExtensionsAction extends Action { export class ShowLanguageExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showLanguageExtensions'; - static SHORT_LABEL = localize('showLanguageExtensionsShort', "Language Extensions"); + static readonly SHORT_LABEL = localize('showLanguageExtensionsShort', "Language Extensions"); constructor( id: string, @@ -1811,7 +1811,7 @@ export class ShowLanguageExtensionsAction extends Action { export class ShowAzureExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.showAzureExtensions'; - static SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions"); + static readonly SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions"); constructor( id: string, @@ -2153,7 +2153,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction { static readonly ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions'; - static LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)"); + static readonly LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)"); constructor( @@ -2189,7 +2189,7 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfi export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction { static readonly ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions'; - static LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)"); + static readonly LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)"); constructor( @@ -2662,7 +2662,7 @@ export class SystemDisabledWarningAction extends ExtensionAction { export class DisableAllAction extends Action { static readonly ID = 'workbench.extensions.action.disableAll'; - static LABEL = localize('disableAll', "Disable All Installed Extensions"); + static readonly LABEL = localize('disableAll', "Disable All Installed Extensions"); constructor( @@ -2687,7 +2687,7 @@ export class DisableAllAction extends Action { export class DisableAllWorkspaceAction extends Action { static readonly ID = 'workbench.extensions.action.disableAllWorkspace'; - static LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace"); + static readonly LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace"); constructor( @@ -2714,7 +2714,7 @@ export class DisableAllWorkspaceAction extends Action { export class EnableAllAction extends Action { static readonly ID = 'workbench.extensions.action.enableAll'; - static LABEL = localize('enableAll', "Enable All Extensions"); + static readonly LABEL = localize('enableAll', "Enable All Extensions"); constructor( @@ -2739,7 +2739,7 @@ export class EnableAllAction extends Action { export class EnableAllWorkspaceAction extends Action { static readonly ID = 'workbench.extensions.action.enableAllWorkspace'; - static LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace"); + static readonly LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace"); constructor( @@ -2766,7 +2766,7 @@ export class EnableAllWorkspaceAction extends Action { export class InstallVSIXAction extends Action { static readonly ID = 'workbench.extensions.action.installVSIX'; - static LABEL = localize('installVSIX', "Install from VSIX..."); + static readonly LABEL = localize('installVSIX', "Install from VSIX..."); constructor( id = InstallVSIXAction.ID, @@ -2818,7 +2818,7 @@ export class InstallVSIXAction extends Action { export class ReinstallAction extends Action { static readonly ID = 'workbench.extensions.action.reinstall'; - static LABEL = localize('reinstall', "Reinstall Extension..."); + static readonly LABEL = localize('reinstall', "Reinstall Extension..."); constructor( id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL, @@ -2884,7 +2884,7 @@ export class ReinstallAction extends Action { export class InstallSpecificVersionOfExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.install.specificVersion'; - static LABEL = localize('install previous version', "Install Specific Version of Extension..."); + static readonly LABEL = localize('install previous version', "Install Specific Version of Extension..."); constructor( id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts index 919c3762319..41bb90eb632 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts @@ -213,7 +213,7 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree { + this.disposables.add(this.onDidChangeSelection(event => { if (event.browserEvent && event.browserEvent instanceof KeyboardEvent) { extensionsWorkdbenchService.open(event.elements[0].extension, false); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 9230ee1708c..bb17633367f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -708,10 +708,10 @@ export class ExtensionsListView extends ViewletPanel { if (count === 0 && this.isBodyVisible()) { if (error) { if (error instanceof ExtensionListViewWarning) { - this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Warning); + this.bodyTemplate.messageSeverityIcon.className = `codicon ${SeverityIcon.className(Severity.Warning)}`; this.bodyTemplate.messageBox.textContent = getErrorMessage(error); } else { - this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Error); + this.bodyTemplate.messageSeverityIcon.className = `codicon ${SeverityIcon.className(Severity.Error)}`; this.bodyTemplate.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error)); } } else { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 087dd63f6f8..c78938f30cc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -14,7 +14,7 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, - InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier + InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, groupByExtension, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -24,7 +24,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { URI } from 'vs/base/common/uri'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; +import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/common/url'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -308,8 +308,8 @@ ${this.description} class Extensions extends Disposable { - private readonly _onChange: Emitter = new Emitter(); - get onChange(): Event { return this._onChange.event; } + private readonly _onChange: Emitter<{ extension: Extension, operation?: InstallOperation } | undefined> = this._register(new Emitter<{ extension: Extension, operation?: InstallOperation } | undefined>()); + get onChange(): Event<{ extension: Extension, operation?: InstallOperation } | undefined> { return this._onChange.event; } private installing: Extension[] = []; private uninstalling: Extension[] = []; @@ -370,7 +370,7 @@ class Extensions extends Disposable { const local = extension.local.metadata ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId }); extension.local = local; extension.gallery = compatible; - this._onChange.fire(extension); + this._onChange.fire({ extension }); return true; } return false; @@ -397,7 +397,7 @@ class Extensions extends Disposable { const extension = this.installed.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0] || this.instantiationService.createInstance(Extension, this.stateProvider, this.server, undefined, gallery); this.installing.push(extension); - this._onChange.fire(extension); + this._onChange.fire({ extension }); } } @@ -421,9 +421,10 @@ class Extensions extends Disposable { if (!extension.gallery) { extension.gallery = gallery; } + extension.enablementState = this.extensionEnablementService.getEnablementState(local); } } - this._onChange.fire(error ? undefined : extension); + this._onChange.fire(error || !extension ? undefined : { extension, operation: event.operation }); } private onUninstallExtension(identifier: IExtensionIdentifier): void { @@ -431,7 +432,7 @@ class Extensions extends Disposable { if (extension) { const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0] || extension; this.uninstalling = [uninstalling, ...this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier))]; - this._onChange.fire(uninstalling); + this._onChange.fire(uninstalling ? { extension: uninstalling } : undefined); } } @@ -442,7 +443,7 @@ class Extensions extends Disposable { const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0]; this.uninstalling = this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier)); if (uninstalling) { - this._onChange.fire(uninstalling); + this._onChange.fire({ extension: uninstalling }); } } @@ -453,7 +454,7 @@ class Extensions extends Disposable { const enablementState = this.extensionEnablementService.getEnablementState(extension.local); if (enablementState !== extension.enablementState) { (extension as Extension).enablementState = enablementState; - this._onChange.fire(extension as Extension); + this._onChange.fire({ extension: extension as Extension }); } } } @@ -506,11 +507,13 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension super(); if (this.extensionManagementServerService.localExtensionManagementServer) { this.localExtensions = this._register(instantiationService.createInstance(Extensions, extensionManagementServerService.localExtensionManagementServer, ext => this.getExtensionState(ext))); - this._register(this.localExtensions.onChange(e => this._onChange.fire(e))); + this._register(this.localExtensions.onChange(e => this._onChange.fire(e ? e.extension : undefined))); + this._register(Event.filter(this.localExtensions.onChange, e => !!e && e.operation === InstallOperation.Install)(e => this.onDidInstallExtension(e!.extension))); } if (this.extensionManagementServerService.remoteExtensionManagementServer) { this.remoteExtensions = this._register(instantiationService.createInstance(Extensions, extensionManagementServerService.remoteExtensionManagementServer, ext => this.getExtensionState(ext))); - this._register(this.remoteExtensions.onChange(e => this._onChange.fire(e))); + this._register(this.remoteExtensions.onChange(e => this._onChange.fire(e ? e.extension : undefined))); + this._register(Event.filter(this.remoteExtensions.onChange, e => !!e && e.operation === InstallOperation.Install)(e => this.onDidInstallExtension(e!.extension))); } this.syncDelayer = new ThrottledDelayer(ExtensionsWorkbenchService.SyncPeriod); @@ -784,7 +787,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (extension instanceof URI) { return this.installWithProgress(async () => { const { identifier } = await this.extensionService.install(extension); - this.checkAndEnableDisabledDependencies(identifier); return this.local.filter(local => areSameExtensions(local.identifier, identifier))[0]; }); } @@ -801,7 +803,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return this.installWithProgress(async () => { await this.installFromGallery(extension, gallery); - this.checkAndEnableDisabledDependencies(gallery.identifier); return this.local.filter(local => areSameExtensions(local.identifier, gallery.identifier))[0]; }, gallery.displayName); } @@ -887,15 +888,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } } - private checkAndEnableDisabledDependencies(extensionIdentifier: IExtensionIdentifier): Promise { - const extension = this.local.filter(e => (e.local || e.gallery) && areSameExtensions(extensionIdentifier, e.identifier))[0]; - if (extension) { - const disabledDepencies = this.getExtensionsRecursively([extension], this.local, EnablementState.EnabledGlobally, { dependencies: true, pack: false }); - if (disabledDepencies.length) { - return this.setEnablement(disabledDepencies, EnablementState.EnabledGlobally); - } - } - return Promise.resolve(); + private onDidInstallExtension(extension: IExtension): void { + this.setEnablement(extension, EnablementState.EnabledGlobally); } private promptAndSetEnablement(extensions: IExtension[], enablementState: EnablementState): Promise { @@ -1048,7 +1042,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.notificationService.error(err); } - handleURL(uri: URI): Promise { + handleURL(uri: URI, options?: IOpenURLOptions): Promise { if (!/^extension/.test(uri.path)) { return Promise.resolve(false); } diff --git a/src/vs/workbench/contrib/extensions/browser/media/language-icon.svg b/src/vs/workbench/contrib/extensions/browser/media/language-icon.svg old mode 100755 new mode 100644 diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts index 79f86a33774..29ae1fd3043 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts @@ -107,12 +107,12 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio } } - public startProfiling(): Promise | null { + public async startProfiling(): Promise { if (this._state !== ProfileSessionState.None) { return null; } - const inspectPort = this._extensionService.getInspectPort(); + const inspectPort = await this._extensionService.getInspectPort(false); if (!inspectPort) { return this._dialogService.confirm({ type: 'info', diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index b4185a6614f..871122fd967 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -14,7 +14,7 @@ import { Schemas } from 'vs/base/common/network'; export class OpenExtensionsFolderAction extends Action { static readonly ID = 'workbench.extensions.action.openExtensionsFolder'; - static LABEL = localize('openExtensionsFolder', "Open Extensions Folder"); + static readonly LABEL = localize('openExtensionsFolder', "Open Extensions Folder"); constructor( id: string, diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts index 56fe69484cd..c5b6f8b87a1 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts @@ -43,7 +43,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont private async _onDidChangeResponsiveChange(event: IResponsiveStateChangeEvent): Promise { - const port = this._extensionService.getInspectPort(); + const port = await this._extensionService.getInspectPort(true); if (!port) { return; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index ffe0daa5e1c..7059fa92238 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -459,7 +459,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { export class ShowRuntimeExtensionsAction extends Action { static readonly ID = 'workbench.action.showRuntimeExtensions'; - static LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions"); + static readonly LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions"); constructor( id: string, label: string, @@ -477,7 +477,7 @@ export class ShowRuntimeExtensionsAction extends Action { export class ReportExtensionIssueAction extends Action { private static readonly _id = 'workbench.extensions.action.reportExtensionIssue'; - private static _label = nls.localize('reportExtensionIssue', "Report Issue"); + private static readonly _label = nls.localize('reportExtensionIssue', "Report Issue"); private readonly _url: string; @@ -533,8 +533,8 @@ export class ReportExtensionIssueAction extends Action { export class DebugExtensionHostAction extends Action { static readonly ID = 'workbench.extensions.action.debugExtensionHost'; - static LABEL = nls.localize('debugExtensionHost', "Start Debugging Extension Host"); - static CSS_CLASS = 'debug-extension-host'; + static readonly LABEL = nls.localize('debugExtensionHost', "Start Debugging Extension Host"); + static readonly CSS_CLASS = 'debug-extension-host'; constructor( @IDebugService private readonly _debugService: IDebugService, @@ -547,7 +547,7 @@ export class DebugExtensionHostAction extends Action { async run(): Promise { - const inspectPort = this._extensionService.getInspectPort(); + const inspectPort = await this._extensionService.getInspectPort(false); if (!inspectPort) { const res = await this._dialogService.confirm({ type: 'info', @@ -572,7 +572,7 @@ export class DebugExtensionHostAction extends Action { export class StartExtensionHostProfileAction extends Action { static readonly ID = 'workbench.extensions.action.extensionHostProfile'; - static LABEL = nls.localize('extensionHostProfileStart', "Start Extension Host Profile"); + static readonly LABEL = nls.localize('extensionHostProfileStart', "Start Extension Host Profile"); constructor( id: string = StartExtensionHostProfileAction.ID, label: string = StartExtensionHostProfileAction.LABEL, @@ -589,7 +589,7 @@ export class StartExtensionHostProfileAction extends Action { export class StopExtensionHostProfileAction extends Action { static readonly ID = 'workbench.extensions.action.stopExtensionHostProfile'; - static LABEL = nls.localize('stopExtensionHostProfileStart', "Stop Extension Host Profile"); + static readonly LABEL = nls.localize('stopExtensionHostProfileStart', "Stop Extension Host Profile"); constructor( id: string = StartExtensionHostProfileAction.ID, label: string = StartExtensionHostProfileAction.LABEL, @@ -606,7 +606,7 @@ export class StopExtensionHostProfileAction extends Action { export class SaveExtensionHostProfileAction extends Action { - static LABEL = nls.localize('saveExtensionHostProfile', "Save Extension Host Profile"); + static readonly LABEL = nls.localize('saveExtensionHostProfile', "Save Extension Host Profile"); static readonly ID = 'workbench.extensions.action.saveExtensionHostProfile'; constructor( @@ -636,7 +636,7 @@ export class SaveExtensionHostProfileAction extends Action { }] }); - if (!picked || !picked.filePath) { + if (!picked || !picked.filePath || picked.canceled) { return; } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index fbaac3cc4f9..d3568cdb3af 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -942,6 +942,46 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); + test('test installing an extension re-eanbles it when disabled globally', async () => { + testObject = await aWorkbenchService(); + const local = aLocalExtension('pub.a'); + await instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + const actual = await testObject.queryLocal(); + assert.equal(actual[0].enablementState, EnablementState.EnabledGlobally); + }); + + test('test updating an extension does not re-eanbles it when disabled globally', async () => { + testObject = await aWorkbenchService(); + const local = aLocalExtension('pub.a'); + await instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + const actual = await testObject.queryLocal(); + assert.equal(actual[0].enablementState, EnablementState.DisabledGlobally); + }); + + test('test installing an extension re-eanbles it when workspace disabled', async () => { + testObject = await aWorkbenchService(); + const local = aLocalExtension('pub.a'); + await instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.DisabledWorkspace); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + const actual = await testObject.queryLocal(); + assert.equal(actual[0].enablementState, EnablementState.EnabledGlobally); + }); + + test('test updating an extension does not re-eanbles it when workspace disabled', async () => { + testObject = await aWorkbenchService(); + const local = aLocalExtension('pub.a'); + await instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.DisabledWorkspace); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); + const actual = await testObject.queryLocal(); + assert.equal(actual[0].enablementState, EnablementState.DisabledWorkspace); + }); + async function aWorkbenchService(): Promise { const workbenchService: ExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); await workbenchService.queryLocal(); diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 2721433b39f..ae6f9bcbbd7 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -142,7 +142,7 @@ export class FeedbackDropdown extends Dropdown { })); disposables.add(dom.addDisposableListener(closeBtn, dom.EventType.MOUSE_OUT, () => { - closeBtn.style.backgroundColor = null; + closeBtn.style.backgroundColor = ''; })); this.invoke(closeBtn, disposables, () => this.hide()); @@ -276,17 +276,17 @@ export class FeedbackDropdown extends Dropdown { disposables.add(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground, editorWidgetForeground, inputBackground, inputForeground, inputBorder, editorBackground, contrastBorder }, colors => { if (this.feedbackForm) { - this.feedbackForm.style.backgroundColor = colors.editorWidgetBackground ? colors.editorWidgetBackground.toString() : null; + this.feedbackForm.style.backgroundColor = colors.editorWidgetBackground ? colors.editorWidgetBackground.toString() : ''; this.feedbackForm.style.color = colors.editorWidgetForeground ? colors.editorWidgetForeground.toString() : null; - this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px ${colors.widgetShadow}` : null; + this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px ${colors.widgetShadow}` : ''; } if (this.feedbackDescriptionInput) { - this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : null; + this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : ''; this.feedbackDescriptionInput.style.color = colors.inputForeground ? colors.inputForeground.toString() : null; this.feedbackDescriptionInput.style.border = `1px solid ${colors.inputBorder || 'transparent'}`; } - contactUsContainer.style.backgroundColor = colors.editorBackground ? colors.editorBackground.toString() : null; + contactUsContainer.style.backgroundColor = colors.editorBackground ? colors.editorBackground.toString() : ''; contactUsContainer.style.border = `1px solid ${colors.contrastBorder || 'transparent'}`; })); diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index 745b55f7806..cfd4b53068a 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -71,7 +71,11 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben if (!this.dropdown) { const statusContainr = document.getElementById('status.feedback'); if (statusContainr) { - this.dropdown = this._register(this.instantiationService.createInstance(FeedbackDropdown, statusContainr.getElementsByClassName('octicon').item(0), { + const icon = statusContainr.getElementsByClassName('octicon').item(0) as HTMLElement | null; + if (!icon) { + throw new Error('Could not find icon'); + } + this.dropdown = this._register(this.instantiationService.createInstance(FeedbackDropdown, icon, { contextViewProvider: this.contextViewService, feedbackService: this.instantiationService.createInstance(TwitterFeedbackService), onFeedbackVisibilityChange: visible => this.entry!.update(this.getStatusEntry(visible)) diff --git a/src/vs/workbench/contrib/feedback/browser/media/twitter.svg b/src/vs/workbench/contrib/feedback/browser/media/twitter.svg old mode 100755 new mode 100644 diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 463b4befe31..06d52c2e01d 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { isFunction } from 'vs/base/common/types'; +import { isFunction, assertIsDefined } from 'vs/base/common/types'; import { isValidBasename } from 'vs/base/common/extpath'; import { basename } from 'vs/base/common/resources'; import { Action } from 'vs/base/common/actions'; @@ -118,7 +118,8 @@ export class TextFileEditor extends BaseTextEditor { setOptions(options: EditorOptions | undefined): void { const textOptions = options as TextEditorOptions; if (textOptions && isFunction(textOptions.apply)) { - textOptions.apply(this.getControl(), ScrollType.Smooth); + const textEditor = assertIsDefined(this.getControl()); + textOptions.apply(textEditor, ScrollType.Smooth); } } @@ -147,7 +148,7 @@ export class TextFileEditor extends BaseTextEditor { const textFileModel = resolvedModel; // Editor - const textEditor = this.getControl(); + const textEditor = assertIsDefined(this.getControl()); textEditor.setModel(textFileModel.textEditorModel); // Always restore View State if any associated @@ -259,7 +260,10 @@ export class TextFileEditor extends BaseTextEditor { this.doSaveOrClearTextEditorViewState(this.input); // Clear Model - this.getControl().setModel(null); + const textEditor = this.getControl(); + if (textEditor) { + textEditor.setModel(null); + } // Pass to super super.clearInput(); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 3cdcb6c65e5..e44827d52c1 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -39,7 +39,7 @@ import { Schemas } from 'vs/base/common/network'; import { IDialogService, IConfirmationResult, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { Constants } from 'vs/editor/common/core/uint'; +import { Constants } from 'vs/base/common/uint'; import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; @@ -125,8 +125,8 @@ export class NewFolderAction extends Action { /* Create new file from anywhere: Open untitled */ export class GlobalNewUntitledFileAction extends Action { - public static readonly ID = 'workbench.action.files.newUntitledFile'; - public static readonly LABEL = nls.localize('newUntitledFile', "New Untitled File"); + static readonly ID = 'workbench.action.files.newUntitledFile'; + static readonly LABEL = nls.localize('newUntitledFile', "New Untitled File"); constructor( id: string, @@ -136,7 +136,7 @@ export class GlobalNewUntitledFileAction extends Action { super(id, label); } - public run(): Promise { + run(): Promise { return this.editorService.openEditor({ options: { pinned: true } }); // untitled are always pinned } } @@ -440,8 +440,8 @@ export function incrementFileName(name: string, isFolder: boolean, incrementalNa // Global Compare with export class GlobalCompareResourcesAction extends Action { - public static readonly ID = 'workbench.files.action.compareFileWith'; - public static readonly LABEL = nls.localize('globalCompareFile', "Compare Active File With..."); + static readonly ID = 'workbench.files.action.compareFileWith'; + static readonly LABEL = nls.localize('globalCompareFile', "Compare Active File With..."); constructor( id: string, @@ -453,7 +453,7 @@ export class GlobalCompareResourcesAction extends Action { super(id, label); } - public run(): Promise { + run(): Promise { const activeInput = this.editorService.activeEditor; const activeResource = activeInput ? activeInput.getResource() : undefined; if (activeResource) { @@ -491,8 +491,8 @@ export class GlobalCompareResourcesAction extends Action { } export class ToggleAutoSaveAction extends Action { - public static readonly ID = 'workbench.action.toggleAutoSave'; - public static readonly LABEL = nls.localize('toggleAutoSave', "Toggle Auto Save"); + static readonly ID = 'workbench.action.toggleAutoSave'; + static readonly LABEL = nls.localize('toggleAutoSave', "Toggle Auto Save"); constructor( id: string, @@ -502,7 +502,7 @@ export class ToggleAutoSaveAction extends Action { super(id, label); } - public run(): Promise { + run(): Promise { const setting = this.configurationService.inspect('files.autoSave'); let userAutoSaveConfig = setting.user; if (types.isUndefinedOrNull(userAutoSaveConfig)) { @@ -562,7 +562,7 @@ export abstract class BaseSaveAllAction extends Action { } } - public run(context?: any): Promise { + run(context?: any): Promise { return this.doRun(context).then(() => true, error => { onError(this.notificationService, error); return false; @@ -572,10 +572,10 @@ export abstract class BaseSaveAllAction extends Action { export class SaveAllAction extends BaseSaveAllAction { - public static readonly ID = 'workbench.action.files.saveAll'; - public static readonly LABEL = SAVE_ALL_LABEL; + static readonly ID = 'workbench.action.files.saveAll'; + static readonly LABEL = SAVE_ALL_LABEL; - public get class(): string { + get class(): string { return 'explorer-action codicon-save-all'; } @@ -590,10 +590,10 @@ export class SaveAllAction extends BaseSaveAllAction { export class SaveAllInGroupAction extends BaseSaveAllAction { - public static readonly ID = 'workbench.files.action.saveAllInGroup'; - public static readonly LABEL = nls.localize('saveAllInGroup', "Save All in Group"); + static readonly ID = 'workbench.files.action.saveAllInGroup'; + static readonly LABEL = nls.localize('saveAllInGroup', "Save All in Group"); - public get class(): string { + get class(): string { return 'explorer-action codicon-save-all'; } @@ -608,22 +608,22 @@ export class SaveAllInGroupAction extends BaseSaveAllAction { export class CloseGroupAction extends Action { - public static readonly ID = 'workbench.files.action.closeGroup'; - public static readonly LABEL = nls.localize('closeGroup', "Close Group"); + static readonly ID = 'workbench.files.action.closeGroup'; + static readonly LABEL = nls.localize('closeGroup', "Close Group"); constructor(id: string, label: string, @ICommandService private readonly commandService: ICommandService) { super(id, label, 'codicon-close-all'); } - public run(context?: any): Promise { + run(context?: any): Promise { return this.commandService.executeCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, {}, context); } } export class FocusFilesExplorer extends Action { - public static readonly ID = 'workbench.files.action.focusFilesExplorer'; - public static readonly LABEL = nls.localize('focusFilesExplorer', "Focus on Files Explorer"); + static readonly ID = 'workbench.files.action.focusFilesExplorer'; + static readonly LABEL = nls.localize('focusFilesExplorer', "Focus on Files Explorer"); constructor( id: string, @@ -633,15 +633,15 @@ export class FocusFilesExplorer extends Action { super(id, label); } - public run(): Promise { + run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true); } } export class ShowActiveFileInExplorer extends Action { - public static readonly ID = 'workbench.files.action.showActiveFileInExplorer'; - public static readonly LABEL = nls.localize('showInExplorer', "Reveal Active File in Side Bar"); + static readonly ID = 'workbench.files.action.showActiveFileInExplorer'; + static readonly LABEL = nls.localize('showInExplorer', "Reveal Active File in Side Bar"); constructor( id: string, @@ -653,7 +653,7 @@ export class ShowActiveFileInExplorer extends Action { super(id, label); } - public run(): Promise { + run(): Promise { const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); if (resource) { this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource); @@ -667,8 +667,8 @@ export class ShowActiveFileInExplorer extends Action { export class CollapseExplorerView extends Action { - public static readonly ID = 'workbench.files.action.collapseExplorerFolders'; - public static readonly LABEL = nls.localize('collapseExplorerFolders', "Collapse Folders in Explorer"); + static readonly ID = 'workbench.files.action.collapseExplorerFolders'; + static readonly LABEL = nls.localize('collapseExplorerFolders', "Collapse Folders in Explorer"); constructor(id: string, label: string, @@ -694,8 +694,8 @@ export class CollapseExplorerView extends Action { export class RefreshExplorerView extends Action { - public static readonly ID = 'workbench.files.action.refreshFilesExplorer'; - public static readonly LABEL = nls.localize('refreshExplorer', "Refresh Explorer"); + static readonly ID = 'workbench.files.action.refreshFilesExplorer'; + static readonly LABEL = nls.localize('refreshExplorer', "Refresh Explorer"); constructor( @@ -710,7 +710,7 @@ export class RefreshExplorerView extends Action { })); } - public run(): Promise { + run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID).then(() => this.explorerService.refresh() ); @@ -719,8 +719,8 @@ export class RefreshExplorerView extends Action { export class ShowOpenedFileInNewWindow extends Action { - public static readonly ID = 'workbench.action.files.showOpenedFileInNewWindow'; - public static readonly LABEL = nls.localize('openFileInNewWindow', "Open Active File in New Window"); + static readonly ID = 'workbench.action.files.showOpenedFileInNewWindow'; + static readonly LABEL = nls.localize('openFileInNewWindow', "Open Active File in New Window"); constructor( id: string, @@ -733,7 +733,7 @@ export class ShowOpenedFileInNewWindow extends Action { super(id, label); } - public run(): Promise { + run(): Promise { const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); if (fileResource) { if (this.fileService.canHandleResource(fileResource)) { @@ -808,8 +808,8 @@ export function getWellFormedFileName(filename: string): string { export class CompareWithClipboardAction extends Action { - public static readonly ID = 'workbench.files.action.compareWithClipboard'; - public static readonly LABEL = nls.localize('compareWithClipboard', "Compare Active File with Clipboard"); + static readonly ID = 'workbench.files.action.compareWithClipboard'; + static readonly LABEL = nls.localize('compareWithClipboard', "Compare Active File with Clipboard"); private static readonly SCHEME = 'clipboardCompare'; @@ -828,7 +828,7 @@ export class CompareWithClipboardAction extends Action { this.enabled = true; } - public run(): Promise { + run(): Promise { const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); if (resource && (this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled)) { if (!this.registrationDisposal) { @@ -848,7 +848,7 @@ export class CompareWithClipboardAction extends Action { return Promise.resolve(true); } - public dispose(): void { + dispose(): void { super.dispose(); dispose(this.registrationDisposal); diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 1f4cd95f65d..8afe9ace396 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -253,6 +253,7 @@ configurationRegistry.registerConfiguration({ }, 'files.eol': { 'type': 'string', + 'overridable': true, 'enum': [ '\n', '\r\n', diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg new file mode 100644 index 00000000000..4862c55dbeb --- /dev/null +++ b/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg new file mode 100644 index 00000000000..05f920b29b6 --- /dev/null +++ b/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg new file mode 100644 index 00000000000..6359b42e623 --- /dev/null +++ b/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 86fd93fa0d3..976b42abd13 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -163,3 +163,16 @@ .hc-black .monaco-workbench .explorer-viewlet .editor-group { line-height: 20px; } + +/* TODO @misolori convert these to use icon font, for the debug viewlet */ +.monaco-workbench .explorer-action.collapse-explorer { + background: url("collapse-all-light.svg") 50% no-repeat; +} + +.vs-dark .monaco-workbench .explorer-action.collapse-explorer { + background: url("collapse-all-dark.svg") 50% no-repeat; +} + +.hc-black .monaco-workbench .explorer-action.collapse-explorer { + background: url("collapse-all-hc.svg") 50% no-repeat; +} diff --git a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts index 608638b5178..fb12ef07f89 100644 --- a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts @@ -141,7 +141,7 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I // Save Elevated if (canHandlePermissionOrReadonlyErrors && (isPermissionDenied || triedToMakeWriteable)) { - primaryActions.push(this.instantiationService.createInstance(SaveElevatedAction, model, triedToMakeWriteable)); + primaryActions.push(this.instantiationService.createInstance(SaveElevatedAction, model, !!triedToMakeWriteable)); } // Overwrite diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index e7e9ecf5f61..f24f0128c6f 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import * as errors from 'vs/base/common/errors'; import * as DOM from 'vs/base/browser/dom'; -import { IAction } from 'vs/base/common/actions'; import { Button } from 'vs/base/browser/ui/button/button'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -34,7 +33,6 @@ export class EmptyView extends ViewletPanel { private button!: Button; private messageElement!: HTMLElement; - private titleElement!: HTMLElement; constructor( options: IViewletViewOptions, @@ -53,15 +51,6 @@ export class EmptyView extends ViewletPanel { this._register(this.labelService.onDidChangeFormatters(() => this.setLabels())); } - renderHeader(container: HTMLElement): void { - const titleContainer = document.createElement('div'); - DOM.addClass(titleContainer, 'title'); - container.appendChild(titleContainer); - - this.titleElement = document.createElement('span'); - titleContainer.appendChild(this.titleElement); - } - protected renderBody(container: HTMLElement): void { DOM.addClass(container, 'explorer-empty-view'); container.tabIndex = 0; @@ -80,8 +69,9 @@ export class EmptyView extends ViewletPanel { if (!this.actionRunner) { return; } - const actionClass = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? AddRootFolderAction : OpenFolderAction; - const action = this.instantiationService.createInstance(actionClass, actionClass.ID, actionClass.LABEL); + const action = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE + ? this.instantiationService.createInstance(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL) + : this.instantiationService.createInstance(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL); this.actionRunner.run(action).then(() => { action.dispose(); }, err => { @@ -110,7 +100,9 @@ export class EmptyView extends ViewletPanel { container.style.backgroundColor = color ? color.toString() : ''; }, onDragOver: e => { - e.dataTransfer!.dropEffect = 'copy'; + if (e.dataTransfer) { + e.dataTransfer.dropEffect = 'copy'; + } } })); @@ -123,7 +115,7 @@ export class EmptyView extends ViewletPanel { if (this.button) { this.button.label = nls.localize('addFolder', "Add Folder"); } - this.titleElement.textContent = EmptyView.NAME; + this.updateTitle(EmptyView.NAME); } else { if (this.environmentService.configuration.remoteAuthority && !isWeb) { const hostLabel = this.labelService.getHostLabel(Schemas.vscodeRemote, this.environmentService.configuration.remoteAuthority); @@ -134,7 +126,7 @@ export class EmptyView extends ViewletPanel { if (this.button) { this.button.label = nls.localize('openFolder', "Open Folder"); } - this.titleElement.textContent = this.title; + this.updateTitle(this.title); } } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index fda43517833..359ad9abfa5 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -101,7 +101,7 @@ export class ExplorerView extends ViewletPanel { this.resourceMoveableToTrash = ExplorerResourceMoveableToTrash.bindTo(contextKeyService); const decorationProvider = new ExplorerDecorationsProvider(this.explorerService, contextService); - decorationService.registerDecorationsProvider(decorationProvider); + this._register(decorationService.registerDecorationsProvider(decorationProvider)); this._register(decorationProvider); this._register(this.resourceContext); } diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 95aa5d90a6e..6e943f670f7 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -49,7 +49,7 @@ export class OpenEditorsView extends ViewletPanel { private static readonly DEFAULT_VISIBLE_OPEN_EDITORS = 9; static readonly ID = 'workbench.explorer.openEditorsView'; - static NAME = nls.localize({ key: 'openEditors', comment: ['Open is an adjective'] }, "Open Editors"); + static readonly NAME = nls.localize({ key: 'openEditors', comment: ['Open is an adjective'] }, "Open Editors"); private dirtyCountElement!: HTMLElement; private listRefreshScheduler: RunOnceScheduler; @@ -185,15 +185,15 @@ export class OpenEditorsView extends ViewletPanel { this.dirtyCountElement = dom.append(count, $('.monaco-count-badge')); this._register((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; - const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : null; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; + const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; + const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : ''; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; this.dirtyCountElement.style.backgroundColor = background; this.dirtyCountElement.style.color = foreground; - this.dirtyCountElement.style.borderWidth = border ? '1px' : null; - this.dirtyCountElement.style.borderStyle = border ? 'solid' : null; + this.dirtyCountElement.style.borderWidth = border ? '1px' : ''; + this.dirtyCountElement.style.borderStyle = border ? 'solid' : ''; this.dirtyCountElement.style.borderColor = border; }))); diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 0072552bf87..c874b1342a2 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -31,8 +31,8 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private static readonly MEMOIZER = createMemoizer(); - private preferredEncoding: string; - private preferredMode: string; + private preferredEncoding: string | undefined; + private preferredMode: string | undefined; private forceOpenAs: ForceOpenAs = ForceOpenAs.None; @@ -82,6 +82,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private onModelOrphanedChanged(e: TextFileModelChangeEvent): void { if (e.resource.toString() === this.resource.toString()) { + FileEditorInput.MEMOIZER.clear(); this._onDidChangeLabel.fire(); } } @@ -90,7 +91,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { return this.resource; } - getEncoding(): string { + getEncoding(): string | undefined { const textModel = this.textFileService.models.get(this.resource); if (textModel) { return textModel.getEncoding(); @@ -99,7 +100,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { return this.preferredEncoding; } - getPreferredEncoding(): string { + getPreferredEncoding(): string | undefined { return this.preferredEncoding; } @@ -209,6 +210,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private decorateLabel(label: string): string { const model = this.textFileService.models.get(this.resource); + if (model && model.hasState(ModelState.ORPHAN)) { return localize('orphanedFile', "{0} (deleted)", label); } diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 8f5470bff77..a55bbfa8ed4 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor'; -import { IFilesConfiguration, FileChangeType, IFileService } from 'vs/platform/files/common/files'; +import { IFilesConfiguration as PlatformIFilesConfiguration, FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; @@ -102,7 +102,7 @@ export const FILE_EDITOR_INPUT_ID = 'workbench.editors.files.fileEditorInput'; export const BINARY_FILE_EDITOR_ID = 'workbench.editors.files.binaryFileEditor'; -export interface IFilesConfiguration extends IFilesConfiguration, IWorkbenchEditorConfiguration { +export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkbenchEditorConfiguration { explorer: { openEditors: { visible: number; diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index 4b726da442f..8142c039ed6 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -33,7 +33,7 @@ type FormattingEditProvider = DocumentFormattingEditProvider | DocumentRangeForm class DefaultFormatter extends Disposable implements IWorkbenchContribution { - static configName = 'editor.defaultFormatter'; + static readonly configName = 'editor.defaultFormatter'; static extensionIds: (string | null)[] = []; static extensionDescriptions: string[] = []; diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index d96fb5eee92..06b20c612ce 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -6,13 +6,11 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; import { ConfigureLocaleAction } from 'vs/workbench/contrib/localizations/browser/localizationsActions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { IExtensionManagementService, DidInstallExtensionEvent, IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -35,7 +33,6 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLocaleAction, export class LocalizationWorkbenchContribution extends Disposable implements IWorkbenchContribution { constructor( - @ILocalizationsService private readonly localizationService: ILocalizationsService, @INotificationService private readonly notificationService: INotificationService, @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -47,26 +44,10 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo @ITelemetryService private readonly telemetryService: ITelemetryService ) { super(); - this.updateLocaleDefintionSchema(); this.checkAndInstall(); - this._register(this.localizationService.onDidLanguagesChange(() => this.updateLocaleDefintionSchema())); this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e))); } - private updateLocaleDefintionSchema(): void { - this.localizationService.getLanguageIds() - .then(languageIds => { - let lowercaseLanguageIds: string[] = []; - languageIds.forEach((languageId) => { - let lowercaseLanguageId = languageId.toLowerCase(); - if (lowercaseLanguageId !== languageId) { - lowercaseLanguageIds.push(lowercaseLanguageId); - } - }); - registerLocaleDefinitionSchema([...languageIds, ...lowercaseLanguageIds]); - }); - } - private onDidInstallExtension(e: DidInstallExtensionEvent): void { if (e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) { const locale = e.local.manifest.contributes.localizations[0].languageId; @@ -79,7 +60,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo [{ label: updateAndRestart ? localize('yes', "Yes") : localize('restart now', "Restart Now"), run: () => { - const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: locale }], true) : Promise.resolve(undefined); + const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.argvResource, [{ key: 'locale', value: locale }], true) : Promise.resolve(undefined); updatePromise.then(() => this.hostService.restart(), e => this.notificationService.error(e)); } }], @@ -225,31 +206,6 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } } -function registerLocaleDefinitionSchema(languages: string[]): void { - const localeDefinitionFileSchemaId = 'vscode://schemas/locale'; - const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); - // Keep en-US since we generated files with that content. - jsonRegistry.registerSchema(localeDefinitionFileSchemaId, { - id: localeDefinitionFileSchemaId, - allowComments: true, - allowTrailingCommas: true, - description: 'Locale Definition file', - type: 'object', - default: { - 'locale': 'en' - }, - required: ['locale'], - properties: { - locale: { - type: 'string', - enum: languages, - description: localize('JsonSchema.locale', 'The UI Language to use.') - } - } - }); -} - -registerLocaleDefinitionSchema(platform.language ? [platform.language] : []); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(LocalizationWorkbenchContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts index cd04dd6149c..b7b2a9030e6 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts @@ -65,7 +65,7 @@ export class ConfigureLocaleAction extends Action { } if (selectedLanguage) { - await this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: selectedLanguage.label }], true); + await this.jsonEditingService.write(this.environmentService.argvResource, [{ key: 'locale', value: selectedLanguage.label }], true); const restart = await this.dialogService.confirm({ type: 'info', message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."), diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index 34e9eed4475..625a8d7836a 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -15,8 +15,8 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic export class SetLogLevelAction extends Action { - static ID = 'workbench.action.setLogLevel'; - static LABEL = nls.localize('setLogLevel', "Set Log Level..."); + static readonly ID = 'workbench.action.setLogLevel'; + static readonly LABEL = nls.localize('setLogLevel', "Set Log Level..."); constructor(id: string, label: string, @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -60,8 +60,8 @@ export class SetLogLevelAction extends Action { export class OpenWindowSessionLogFileAction extends Action { - static ID = 'workbench.action.openSessionLogFile'; - static LABEL = nls.localize('openSessionLogFile', "Open Window Log File (Session)..."); + static readonly ID = 'workbench.action.openSessionLogFile'; + static readonly LABEL = nls.localize('openSessionLogFile', "Open Window Log File (Session)..."); constructor(id: string, label: string, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, diff --git a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts index 547558b7deb..f2287b85265 100644 --- a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts @@ -12,8 +12,8 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; export class OpenLogsFolderAction extends Action { - static ID = 'workbench.action.openLogsFolder'; - static LABEL = nls.localize('openLogsFolder', "Open Logs Folder"); + static readonly ID = 'workbench.action.openLogsFolder'; + static readonly LABEL = nls.localize('openLogsFolder', "Open Logs Folder"); constructor(id: string, label: string, @IEnvironmentService private readonly environmentService: IEnvironmentService, diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index 2c2edfc7e3e..c509e857100 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -117,7 +117,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations)); // actions - this.collapseAllAction = this._register(new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action collapse-all', true, async () => this.collapseAll())); + this.collapseAllAction = this._register(new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action codicon-collapse-all', true, async () => this.collapseAll())); this.filterAction = this._register(this.instantiationService.createInstance(MarkersFilterAction, { filterText: this.panelState['filter'] || '', filterHistory: this.panelState['filterHistory'] || [], useFilesExclude: !!this.panelState['useFilesExclude'] })); } diff --git a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts index b2498c05d82..cfba514af3f 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts @@ -196,14 +196,14 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { private createBadge(container: HTMLElement): void { const filterBadge = this.filterBadge = DOM.append(container, DOM.$('.markers-panel-filter-badge')); this._register(attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; - const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : null; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; + const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; + const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : ''; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; filterBadge.style.backgroundColor = background; - filterBadge.style.borderWidth = border ? '1px' : null; - filterBadge.style.borderStyle = border ? 'solid' : null; + filterBadge.style.borderWidth = border ? '1px' : ''; + filterBadge.style.borderStyle = border ? 'solid' : ''; filterBadge.style.borderColor = border; filterBadge.style.color = foreground; })); diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 90158388b30..697bfbcff29 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -267,7 +267,7 @@ class MarkerWidget extends Disposable { this.disposables.clear(); dom.clearNode(this.messageAndDetailsContainer); - this.icon.className = `marker-icon ${SeverityIcon.className(MarkerSeverity.toSeverity(element.marker.severity))}`; + this.icon.className = `marker-icon codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(element.marker.severity))}`; this.renderQuickfixActionbar(element); this.renderMultilineActionbar(element); diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index cdec9f47999..cf54727d396 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -132,9 +132,15 @@ margin-left: 6px; } -.markers-panel .monaco-tl-contents .marker-icon, -.markers-panel .monaco-tl-contents .actions .action-item { +.markers-panel .monaco-tl-contents .marker-icon { margin-right: 6px; + display: flex; + align-items: center; + justify-content: center; +} + +.markers-panel .monaco-tl-contents .actions .action-item { + margin-right: 2px; } .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-source, diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index 57cda74ad2d..d9fba5aba15 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -50,6 +50,136 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'description': localize('outline.problems.badges', "Use badges for Errors & Warnings."), 'type': 'boolean', 'default': true + }, + 'outline.filteredTypes.file': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.file', "When set to `false` outline never shows `file`-symbols.") + }, + 'outline.filteredTypes.module': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.module', "When set to `false` outline never shows `module`-symbols.") + }, + 'outline.filteredTypes.namespace': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.namespace', "When set to `false` outline never shows `namespace`-symbols.") + }, + 'outline.filteredTypes.package': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.package', "When set to `false` outline never shows `package`-symbols.") + }, + 'outline.filteredTypes.class': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.class', "When set to `false` outline never shows `class`-symbols.") + }, + 'outline.filteredTypes.method': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.method', "When set to `false` outline never shows `method`-symbols.") + }, + 'outline.filteredTypes.property': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.property', "When set to `false` outline never shows `property`-symbols.") + }, + 'outline.filteredTypes.field': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.field', "When set to `false` outline never shows `field`-symbols.") + }, + 'outline.filteredTypes.constructor': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.constructor', "When set to `false` outline never shows `constructor`-symbols.") + }, + 'outline.filteredTypes.enum': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.enum', "When set to `false` outline never shows `enum`-symbols.") + }, + 'outline.filteredTypes.interface': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.interface', "When set to `false` outline never shows `interface`-symbols.") + }, + 'outline.filteredTypes.function': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.function', "When set to `false` outline never shows `function`-symbols.") + }, + 'outline.filteredTypes.variable': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.variable', "When set to `false` outline never shows `variable`-symbols.") + }, + 'outline.filteredTypes.constant': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.constant', "When set to `false` outline never shows `constant`-symbols.") + }, + 'outline.filteredTypes.string': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.string', "When set to `false` outline never shows `string`-symbols.") + }, + 'outline.filteredTypes.number': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.number', "When set to `false` outline never shows `number`-symbols.") + }, + 'outline.filteredTypes.boolean': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.boolean', "When set to `false` outline never shows `boolean`-symbols.") + }, + 'outline.filteredTypes.array': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.array', "When set to `false` outline never shows `array`-symbols.") + }, + 'outline.filteredTypes.object': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.object', "When set to `false` outline never shows `object`-symbols.") + }, + 'outline.filteredTypes.key': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.key', "When set to `false` outline never shows `key`-symbols.") + }, + 'outline.filteredTypes.null': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.null', "When set to `false` outline never shows `null`-symbols.") + }, + 'outline.filteredTypes.enumMember': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.enumMember', "When set to `false` outline never shows `enumMember`-symbols.") + }, + 'outline.filteredTypes.struct': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.struct', "When set to `false` outline never shows `struct`-symbols.") + }, + 'outline.filteredTypes.event': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.event', "When set to `false` outline never shows `event`-symbols.") + }, + 'outline.filteredTypes.operator': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.operator', "When set to `false` outline never shows `operator`-symbols.") + }, + 'outline.filteredTypes.typeParameter': { + type: 'boolean', + default: true, + markdownDescription: localize('filteredTypes.typeParameter', "When set to `false` outline never shows `typeParameter`-symbols.") } } }); diff --git a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts index 9ee2c27c02d..720682801c1 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts @@ -40,7 +40,7 @@ import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { OutlineConfigKeys, OutlineViewFocused, OutlineViewFiltered } from 'vs/editor/contrib/documentSymbols/outline'; import { FuzzyScore } from 'vs/base/common/filters'; -import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider } from 'vs/editor/contrib/documentSymbols/outlineTree'; +import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineFilter } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { basename } from 'vs/base/common/resources'; @@ -247,10 +247,9 @@ export class OutlinePanel extends ViewletPanel { private _treeDataSource!: OutlineDataSource; private _treeRenderer!: OutlineElementRenderer; private _treeComparator!: OutlineItemComparator; + private _treeFilter!: OutlineFilter; private _treeStates = new LRUCache(10); - private _treeFakeUIEvent = new UIEvent('me'); - private readonly _contextKeyFocused: IContextKey; private readonly _contextKeyFiltered: IContextKey; @@ -315,6 +314,7 @@ export class OutlinePanel extends ViewletPanel { this._treeRenderer = this._instantiationService.createInstance(OutlineElementRenderer); this._treeDataSource = new OutlineDataSource(); this._treeComparator = new OutlineItemComparator(this._outlineViewState.sortBy); + this._treeFilter = this._instantiationService.createInstance(OutlineFilter, 'outline.filteredTypes'); this._tree = this._instantiationService.createInstance( WorkbenchDataTree, 'OutlinePanel', @@ -328,6 +328,7 @@ export class OutlinePanel extends ViewletPanel { multipleSelectionSupport: false, filterOnType: this._outlineViewState.filterOnType, sorter: this._treeComparator, + filter: this._treeFilter, identityProvider: new OutlineIdentityProvider(), keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider() } @@ -366,6 +367,10 @@ export class OutlinePanel extends ViewletPanel { if (e.affectsConfiguration(OutlineConfigKeys.icons)) { this._tree.updateChildren(); } + if (e.affectsConfiguration('outline.filteredTypes')) { + this._treeFilter.update(); + this._tree.refilter(); + } })); this._register(this.onDidChangeBodyVisibility(visible => { @@ -447,7 +452,6 @@ export class OutlinePanel extends ViewletPanel { private async _doUpdate(editor: ICodeEditor | undefined, event: IModelContentChangedEvent | undefined): Promise { this._editorDisposables.clear(); - this._progressBar.infinite().show(150); const oldModel = this._tree.getInput(); @@ -460,16 +464,16 @@ export class OutlinePanel extends ViewletPanel { return this._showMessage(localize('no-editor', "The active editor cannot provide outline information.")); } - let textModel = editor.getModel(); - let loadingMessage: IDisposable | undefined; - if (!oldModel) { - loadingMessage = new TimeoutTimer( - () => this._showMessage(localize('loading', "Loading document symbols for '{0}'...", basename(textModel.uri))), - 100 - ); - } + const textModel = editor.getModel(); + const loadingMessage = oldModel && new TimeoutTimer( + () => this._showMessage(localize('loading', "Loading document symbols for '{0}'...", basename(textModel.uri))), + 100 + ); - let createdModel = await OutlinePanel._createOutlineModel(textModel, this._editorDisposables); + const requestDelay = OutlineModel.getRequestDelay(textModel); + this._progressBar.infinite().show(requestDelay); + + const createdModel = await OutlinePanel._createOutlineModel(textModel, this._editorDisposables); dispose(loadingMessage); if (!createdModel) { return; @@ -519,7 +523,7 @@ export class OutlinePanel extends ViewletPanel { newModel = oldModel; } else { let state = this._treeStates.get(newModel.textModel.uri.toString()); - await this._tree.setInput(newModel, state); + this._tree.setInput(newModel, state); } // transfer focus from domNode to the tree @@ -531,10 +535,8 @@ export class OutlinePanel extends ViewletPanel { // feature: reveal outline selection in editor // on change -> reveal/select defining range - this._editorDisposables.add(this._tree.onDidChangeSelection(e => { - if (e.browserEvent === this._treeFakeUIEvent /* || e.payload && e.payload.didClickOnTwistie */) { - return; - } + this._editorDisposables.add(this._tree.onDidOpen(e => { + let [first] = e.elements; if (!(first instanceof OutlineElement)) { return; @@ -636,7 +638,7 @@ export class OutlinePanel extends ViewletPanel { if (top === null) { this._tree.reveal(item, 0.5); } - this._tree.setFocus([item], this._treeFakeUIEvent); - this._tree.setSelection([item], this._treeFakeUIEvent); + this._tree.setFocus([item]); + this._tree.setSelection([item]); } } diff --git a/src/vs/workbench/contrib/output/browser/outputActions.ts b/src/vs/workbench/contrib/output/browser/outputActions.ts index 3760d018bf5..88e7d5b6e96 100644 --- a/src/vs/workbench/contrib/output/browser/outputActions.ts +++ b/src/vs/workbench/contrib/output/browser/outputActions.ts @@ -213,8 +213,8 @@ export class OpenLogOutputFile extends Action { export class ShowLogsOutputChannelAction extends Action { - static ID = 'workbench.action.showLogs'; - static LABEL = nls.localize('showLogs', "Show Logs..."); + static readonly ID = 'workbench.action.showLogs'; + static readonly LABEL = nls.localize('showLogs', "Show Logs..."); constructor(id: string, label: string, @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -243,8 +243,8 @@ interface IOutputChannelQuickPickItem extends IQuickPickItem { export class OpenOutputLogFileAction extends Action { - static ID = 'workbench.action.openLogFile'; - static LABEL = nls.localize('openLogFile', "Open Log File..."); + static readonly ID = 'workbench.action.openLogFile'; + static readonly LABEL = nls.localize('openLogFile', "Open Log File..."); constructor(id: string, label: string, @IQuickInputService private readonly quickInputService: IQuickInputService, diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index 1a3e692d6e7..940441708e4 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -155,7 +155,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo } } - private onDidPanelOpen(panel: IPanel | null, preserveFocus: boolean): Promise { + private onDidPanelOpen(panel: IPanel | undefined, preserveFocus: boolean): Promise { if (panel && panel.getId() === OUTPUT_PANEL_ID) { this._outputPanel = this.panelService.getActivePanel(); if (this.activeChannel) { diff --git a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts index ee681d2dccf..b61cccb5e95 100644 --- a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts @@ -12,6 +12,7 @@ import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { isWindows } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; +import { find } from 'vs/base/common/arrays'; export interface ICreateData { workspaceFolders: string[]; @@ -44,15 +45,9 @@ export class OutputLinkComputer { }); } - private getModel(uri: string): IMirrorModel | null { + private getModel(uri: string): IMirrorModel | undefined { const models = this.ctx.getMirrorModels(); - for (const model of models) { - if (model.uri.toString() === uri) { - return model; - } - } - - return null; + return find(models, model => model.uri.toString() === uri); } public computeLinks(uri: string): Promise { diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts index 81433e42323..578cc39ab59 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts @@ -59,6 +59,9 @@ export class KeybindingsSearchWidget extends SearchWidget { super(parent, options, contextViewService, instantiationService, themeService); this._register(attachInputBoxStyler(this.inputBox, themeService)); this._register(toDisposable(() => this.stopRecordingKeys())); + this._firstPart = null; + this._chordPart = null; + this._inputValue = ''; this._reset(); } @@ -166,7 +169,45 @@ export class DefineKeybindingWidget extends Widget { @IThemeService private readonly themeService: IThemeService ) { super(); - this.create(); + + this._domNode = createFastDomNode(document.createElement('div')); + this._domNode.setDisplay('none'); + this._domNode.setClassName('defineKeybindingWidget'); + this._domNode.setWidth(DefineKeybindingWidget.WIDTH); + this._domNode.setHeight(DefineKeybindingWidget.HEIGHT); + + const message = nls.localize('defineKeybinding.initial', "Press desired key combination and then press ENTER."); + dom.append(this._domNode.domNode, dom.$('.message', undefined, message)); + + this._register(attachStylerCallback(this.themeService, { editorWidgetBackground, editorWidgetForeground, widgetShadow }, colors => { + if (colors.editorWidgetBackground) { + this._domNode.domNode.style.backgroundColor = colors.editorWidgetBackground.toString(); + } else { + this._domNode.domNode.style.backgroundColor = ''; + } + if (colors.editorWidgetForeground) { + this._domNode.domNode.style.color = colors.editorWidgetForeground.toString(); + } else { + this._domNode.domNode.style.color = null; + } + + if (colors.widgetShadow) { + this._domNode.domNode.style.boxShadow = `0 2px 8px ${colors.widgetShadow}`; + } else { + this._domNode.domNode.style.boxShadow = ''; + } + })); + + this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, this._domNode.domNode, { ariaLabel: message })); + this._keybindingInputWidget.startRecordingKeys(); + this._register(this._keybindingInputWidget.onKeybinding(keybinding => this.onKeybinding(keybinding))); + this._register(this._keybindingInputWidget.onEnter(() => this.hide())); + this._register(this._keybindingInputWidget.onEscape(() => this.onCancel())); + this._register(this._keybindingInputWidget.onBlur(() => this.onCancel())); + + this._outputNode = dom.append(this._domNode.domNode, dom.$('.output')); + this._showExistingKeybindingsNode = dom.append(this._domNode.domNode, dom.$('.existing')); + if (parent) { dom.append(parent, this._domNode.domNode); } @@ -217,46 +258,6 @@ export class DefineKeybindingWidget extends Widget { } } - private create(): void { - this._domNode = createFastDomNode(document.createElement('div')); - this._domNode.setDisplay('none'); - this._domNode.setClassName('defineKeybindingWidget'); - this._domNode.setWidth(DefineKeybindingWidget.WIDTH); - this._domNode.setHeight(DefineKeybindingWidget.HEIGHT); - - const message = nls.localize('defineKeybinding.initial', "Press desired key combination and then press ENTER."); - dom.append(this._domNode.domNode, dom.$('.message', undefined, message)); - - this._register(attachStylerCallback(this.themeService, { editorWidgetBackground, editorWidgetForeground, widgetShadow }, colors => { - if (colors.editorWidgetBackground) { - this._domNode.domNode.style.backgroundColor = colors.editorWidgetBackground.toString(); - } else { - this._domNode.domNode.style.backgroundColor = null; - } - if (colors.editorWidgetForeground) { - this._domNode.domNode.style.color = colors.editorWidgetForeground.toString(); - } else { - this._domNode.domNode.style.color = null; - } - - if (colors.widgetShadow) { - this._domNode.domNode.style.boxShadow = `0 2px 8px ${colors.widgetShadow}`; - } else { - this._domNode.domNode.style.boxShadow = null; - } - })); - - this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, this._domNode.domNode, { ariaLabel: message })); - this._keybindingInputWidget.startRecordingKeys(); - this._register(this._keybindingInputWidget.onKeybinding(keybinding => this.onKeybinding(keybinding))); - this._register(this._keybindingInputWidget.onEnter(() => this.hide())); - this._register(this._keybindingInputWidget.onEscape(() => this.onCancel())); - this._register(this._keybindingInputWidget.onBlur(() => this.onCancel())); - - this._outputNode = dom.append(this._domNode.domNode, dom.$('.output')); - this._showExistingKeybindingsNode = dom.append(this._domNode.domNode, dom.$('.existing')); - } - private onKeybinding(keybinding: [ResolvedKeybinding | null, ResolvedKeybinding | null]): void { const [firstPart, chordPart] = keybinding; this._firstPart = firstPart; diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 8d06fc22dcf..eea6c420e1c 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -29,7 +29,6 @@ import { } from 'vs/workbench/contrib/preferences/common/preferences'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; -import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -79,7 +78,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor private keybindingsListContainer: HTMLElement; private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry | null; private listEntries: IListEntry[]; - private keybindingsList: List; + private keybindingsList: WorkbenchList; private dimension: DOM.Dimension; private delayedFiltering: Delayer; @@ -402,13 +401,13 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor const recordingBadge = DOM.append(container, DOM.$('.recording-badge.disabled')); recordingBadge.textContent = localize('recording', "Recording Keys"); this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder, badgeForeground }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; - const color = colors.badgeForeground ? colors.badgeForeground.toString() : null; + const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; + const color = colors.badgeForeground ? colors.badgeForeground.toString() : ''; recordingBadge.style.backgroundColor = background; - recordingBadge.style.borderWidth = border ? '1px' : null; - recordingBadge.style.borderStyle = border ? 'solid' : null; + recordingBadge.style.borderWidth = border ? '1px' : ''; + recordingBadge.style.borderStyle = border ? 'solid' : ''; recordingBadge.style.borderColor = border; recordingBadge.style.color = color ? color.toString() : null; })); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 9b45e34dee4..006e90f2a76 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -23,14 +23,13 @@ import { parseTree, Node } from 'vs/base/common/json'; import { ScanCodeBinding } from 'vs/base/common/scanCode'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { WindowsNativeResolvedKeybinding } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; -import { themeColorFromId, ThemeColor, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService'; import { overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry'; import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness, OverviewRulerLane } from 'vs/editor/common/model'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; -import Severity from 'vs/base/common/severity'; -import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { equals } from 'vs/base/common/arrays'; const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding"); const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); @@ -267,18 +266,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { const aParts = KeybindingParser.parseUserBinding(a); const bParts = KeybindingParser.parseUserBinding(b); - - if (aParts.length !== bParts.length) { - return false; - } - - for (let i = 0, len = aParts.length; i < len; i++) { - if (!this._userBindingEquals(aParts[i], bParts[i])) { - return false; - } - } - - return true; + return equals(aParts, bParts, (a, b) => this._userBindingEquals(a, b)); } private static _userBindingEquals(a: SimpleKeybinding | ScanCodeBinding, b: SimpleKeybinding | ScanCodeBinding): boolean { @@ -399,8 +387,3 @@ function isInterestingEditorModel(editor: ICodeEditor): boolean { registerEditorContribution(DefineKeybindingController); registerEditorCommand(new DefineKeybindingCommand()); - -registerThemingParticipant((theme, collector) => { - collector.addRule(`.monaco-editor .inlineKeybindingInfo:before { background: url("data:image/svg+xml,${SeverityIcon.getSVGData(Severity.Info, theme)}") -0.1em -0.2em no-repeat; }`); - collector.addRule(`.monaco-editor .inlineKeybindingError:before { background: url("data:image/svg+xml,${SeverityIcon.getSVGData(Severity.Error, theme)}") -0.1em -0.2em no-repeat; }`); -}); diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index b0dff5ac885..276965c3f63 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -16,6 +16,10 @@ color: inherit !important; } +.settings-editor:focus { + outline: none !important; +} + /* header styling */ .settings-editor > .settings-header { box-sizing: border-box; @@ -158,7 +162,7 @@ } .settings-editor > .settings-body .settings-tree-container .monaco-list-row .mouseover .setting-toolbar-container > .monaco-toolbar .codicon-more, -.settings-editor > .settings-body .settings-tree-container .monaco-list-row .setting-item.focused .setting-toolbar-container > .monaco-toolbar .codicon-more, +.settings-editor > .settings-body .settings-tree-container .monaco-list-row .setting-item-contents.focused .setting-toolbar-container > .monaco-toolbar .codicon-more, .settings-editor > .settings-body .settings-tree-container .monaco-list-row .setting-toolbar-container:hover > .monaco-toolbar .codicon-more, .settings-editor > .settings-body .settings-tree-container .monaco-list-row .setting-toolbar-container > .monaco-toolbar .active .codicon-more { opacity: 1; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index b93c9b2fd62..b7742712238 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -198,9 +198,6 @@ export class OpenFolderSettingsAction extends Action { static readonly ID = 'workbench.action.openFolderSettings'; static readonly LABEL = OPEN_FOLDER_SETTINGS_LABEL; - private disposables: IDisposable[] = []; - - constructor( id: string, label: string, @@ -210,8 +207,8 @@ export class OpenFolderSettingsAction extends Action { ) { super(id, label); this.update(); - this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables); - this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.update(), this, this.disposables); + this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this)); + this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.update(), this)); } private update(): void { @@ -228,11 +225,6 @@ export class OpenFolderSettingsAction extends Action { return undefined; }); } - - dispose(): void { - this.disposables = dispose(this.disposables); - super.dispose(); - } } export class ConfigureLanguageBasedSettingsAction extends Action { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 876c94e15cc..2c8cfd12820 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -54,7 +54,7 @@ import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbe import { DefaultSettingsEditorModel, SettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; +import { withNullAsUndefined, withUndefinedAsNull, assertIsDefined } from 'vs/base/common/types'; export class PreferencesEditor extends BaseEditor { @@ -683,7 +683,7 @@ class PreferencesRenderersController extends Disposable { } private _updatePreference(key: string, value: any, source: ISetting, fromEditableSettings?: boolean): void { - const data: { [key: string]: any } = { + const data: { [key: string]: any; } = { userConfigurationKeys: [key] }; @@ -718,7 +718,7 @@ class PreferencesRenderersController extends Disposable { this.telemetryService.publicLog('defaultSettingsActions.copySetting', data); } - private _findSetting(filterResult: IFilterResult, key: string): { groupIdx: number, settingIdx: number, overallSettingIdx: number } | undefined { + private _findSetting(filterResult: IFilterResult, key: string): { groupIdx: number, settingIdx: number, overallSettingIdx: number; } | undefined { let overallSettingIdx = 0; for (let groupIdx = 0; groupIdx < filterResult.filteredGroups.length; groupIdx++) { @@ -826,11 +826,7 @@ class SideBySidePreferencesWidget extends Widget { this._register(attachStylerCallback(this.themeService, { scrollbarShadow }, colors => { const shadow = colors.scrollbarShadow ? colors.scrollbarShadow.toString() : null; - if (shadow) { - this.editablePreferencesEditorContainer.style.boxShadow = `-6px 0 5px -5px ${shadow}`; - } else { - this.editablePreferencesEditorContainer.style.boxShadow = null; - } + this.editablePreferencesEditorContainer.style.boxShadow = shadow ? `-6px 0 5px -5px ${shadow}` : ''; })); this.splitview.addView({ @@ -845,7 +841,7 @@ class SideBySidePreferencesWidget extends Widget { this._register(focusTracker.onDidFocus(() => this._onFocus.fire())); } - setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer }> { + setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer; }> { this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput); this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource()!); return Promise.all([ @@ -1049,20 +1045,25 @@ export class DefaultPreferencesEditor extends BaseTextEditor { return; } - this.getControl().setModel((editorModel).textEditorModel); + const editor = assertIsDefined(this.getControl()); + editor.setModel((editorModel).textEditorModel); })); } clearInput(): void { // Clear Model - this.getControl().setModel(null); + const editor = this.getControl(); + if (editor) { + editor.setModel(null); + } // Pass to super super.clearInput(); } layout(dimension: DOM.Dimension) { - this.getControl().layout(dimension); + const editor = assertIsDefined(this.getControl()); + editor.layout(dimension); } protected getAriaLabel(): string { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index 2881d09fec2..2b8d5a82f17 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -32,6 +32,7 @@ import { DefaultSettingsEditorModel, SettingsEditorModel, WorkspaceConfiguration import { IMarkerService, IMarkerData, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { find } from 'vs/base/common/arrays'; export interface IPreferencesRenderer extends IDisposable { readonly preferencesModel: IPreferencesEditorModel; @@ -321,12 +322,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR const { key, overrideOf } = setting; if (overrideOf) { const setting = this.getSetting(overrideOf); - for (const override of setting!.overrides!) { - if (override.key === key) { - return override; - } - } - return undefined; + return find(setting!.overrides!, override => override.key === key); } const settingsGroups = this.filterResult ? this.filterResult.filteredGroups : this.preferencesModel.settingsGroups; return this.getPreference(key, settingsGroups); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index b79486ab07d..bec3cdc2210 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -34,6 +34,7 @@ import { PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIV import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { isEqual } from 'vs/base/common/resources'; export class SettingsHeaderWidget extends Widget implements IViewZone { @@ -387,7 +388,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem { const oldFolder = this._folder; const workspace = this.contextService.getWorkspace(); if (oldFolder) { - this._folder = workspace.folders.filter(folder => folder.uri.toString() === oldFolder.uri.toString())[0] || workspace.folders[0]; + this._folder = workspace.folders.filter(folder => isEqual(folder.uri, oldFolder.uri))[0] || workspace.folders[0]; } this._folder = this._folder ? this._folder : workspace.folders.length === 1 ? workspace.folders[0] : null; @@ -440,7 +441,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem { return { id: 'folderSettingsTarget' + index, label: this.labelWithCount(folder.name, folderCount), - checked: this.folder && this.folder.uri.toString() === folder.uri.toString(), + checked: this.folder && isEqual(this.folder.uri, folder.uri), enabled: true, run: () => this._action.run(folder) }; @@ -574,7 +575,7 @@ export class SettingsTargetsWidget extends Widget { const isSameTarget = this.settingsTarget === settingsTarget || settingsTarget instanceof URI && this.settingsTarget instanceof URI && - this.settingsTarget.toString() === settingsTarget.toString(); + isEqual(this.settingsTarget, settingsTarget); if (!isSameTarget) { this.settingsTarget = settingsTarget; @@ -632,13 +633,13 @@ export class SearchWidget extends Widget { if (this.options.showResultCount) { this.countElement = DOM.append(this.controlsDiv, DOM.$('.settings-count-widget')); this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; + const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; this.countElement.style.backgroundColor = background; - this.countElement.style.borderWidth = border ? '1px' : null; - this.countElement.style.borderStyle = border ? 'solid' : null; + this.countElement.style.borderWidth = border ? '1px' : ''; + this.countElement.style.borderStyle = border ? 'solid' : ''; this.countElement.style.borderColor = border; const color = this.themeService.getTheme().getColor(badgeForeground); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 7e3bca1e76f..58f1c7f4c50 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -201,7 +201,7 @@ export class SettingsEditor2 extends BaseEditor { createEditor(parent: HTMLElement): void { parent.setAttribute('tabindex', '-1'); - this.rootElement = DOM.append(parent, $('.settings-editor')); + this.rootElement = DOM.append(parent, $('.settings-editor', { tabindex: '-1' })); this.createHeader(this.rootElement); this.createBody(this.rootElement); @@ -400,15 +400,15 @@ export class SettingsEditor2 extends BaseEditor { this.countElement = DOM.append(searchContainer, DOM.$('.settings-count-widget')); this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder, badgeForeground }, colors => { - const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; - const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; - const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : null; + const background = colors.badgeBackground ? colors.badgeBackground.toString() : ''; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : ''; + const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : ''; this.countElement.style.backgroundColor = background; this.countElement.style.color = foreground; - this.countElement.style.borderWidth = border ? '1px' : null; - this.countElement.style.borderStyle = border ? 'solid' : null; + this.countElement.style.borderWidth = border ? '1px' : ''; + this.countElement.style.borderStyle = border ? 'solid' : ''; this.countElement.style.borderColor = border; })); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 4931271523c..8f9829317ff 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -37,7 +37,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; @@ -394,15 +394,12 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre }); toolbar.setActions([], this.settingActions)(); - // change icon from ellipsis to gear - let icon = container.querySelector('.codicon-more'); - if (icon) { - (icon).classList.add('codicon-gear'); - } - - const button = container.querySelector('.toolbar-toggle-more'); + const button = container.querySelector('.codicon-more'); if (button) { (button).tabIndex = -1; + + // change icon from ellipsis to gear + (button).classList.add('codicon-gear'); } return toolbar; @@ -416,7 +413,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const setting = element.setting; DOM.toggleClass(template.containerElement, 'is-configured', element.isConfigured); - DOM.toggleClass(template.containerElement, 'is-expanded', true); template.containerElement.setAttribute(AbstractSettingRenderer.SETTING_KEY_ATTR, element.setting.key); template.containerElement.setAttribute(AbstractSettingRenderer.SETTING_ID_ATTR, element.id); @@ -1219,7 +1215,7 @@ export class SettingTreeRenderers { } showContextMenu(element: SettingsTreeSettingElement, settingDOMElement: HTMLElement): void { - const toolbarElement = settingDOMElement.querySelector('.toolbar-toggle-more'); + const toolbarElement = settingDOMElement.querySelector('.monaco-toolbar'); if (toolbarElement) { this._contextMenuService.showContextMenu({ getActions: () => this.settingActions, @@ -1544,20 +1540,20 @@ export class SettingsTree extends ObjectTree { this.getHTMLElement().classList.add(treeClass); this.disposables.push(attachStyler(themeService, { - listActiveSelectionBackground: 'transparent', + listActiveSelectionBackground: transparent(Color.white, 0), listActiveSelectionForeground: foreground, - listFocusAndSelectionBackground: 'transparent', + listFocusAndSelectionBackground: transparent(Color.white, 0), listFocusAndSelectionForeground: foreground, - listFocusBackground: 'transparent', + listFocusBackground: transparent(Color.white, 0), listFocusForeground: foreground, listHoverForeground: foreground, - listHoverBackground: 'transparent', - listHoverOutline: 'transparent', - listFocusOutline: 'transparent', - listInactiveSelectionBackground: 'transparent', + listHoverBackground: transparent(Color.white, 0), + listHoverOutline: transparent(Color.white, 0), + listFocusOutline: transparent(Color.white, 0), + listInactiveSelectionBackground: transparent(Color.white, 0), listInactiveSelectionForeground: foreground, - listInactiveFocusBackground: 'transparent', - listInactiveFocusOutline: 'transparent' + listInactiveFocusBackground: transparent(Color.white, 0), + listInactiveFocusOutline: transparent(Color.white, 0) }, colors => { this.style(colors); })); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index eb76fe7dbd4..0ee5430c0e4 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -90,7 +90,7 @@ export class SettingsTreeNewExtensionsElement extends SettingsTreeElement { } export class SettingsTreeSettingElement extends SettingsTreeElement { - private static MAX_DESC_LINES = 20; + private static readonly MAX_DESC_LINES = 20; setting: ISetting; @@ -273,13 +273,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { return false; } - for (let extensionId of extensionFilters) { - if (extensionId.toLowerCase() === this.setting.extensionInfo.id.toLowerCase()) { - return true; - } - } - - return false; + return Array.from(extensionFilters).some(extensionId => extensionId.toLowerCase() === this.setting.extensionInfo!.id.toLowerCase()); } } @@ -409,7 +403,7 @@ function sanitizeId(id: string): string { return id.replace(/[\.\/]/, '_'); } -export function settingKeyToDisplayFormat(key: string, groupId = ''): { category: string, label: string } { +export function settingKeyToDisplayFormat(key: string, groupId = ''): { category: string, label: string; } { const lastDotIdx = key.lastIndexOf('.'); let category = ''; if (lastDotIdx >= 0) { diff --git a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts index a0090bae844..334b144c0db 100644 --- a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; -import * as types from 'vs/base/common/types'; +import { localize } from 'vs/nls'; +import { distinct } from 'vs/base/common/arrays'; +import { withNullAsUndefined, isFunction } from 'vs/base/common/types'; import { Language } from 'vs/base/common/platform'; import { Action, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { Mode, IEntryRunContext, IAutoFocus, IModel, IQuickNavigateConfiguration } from 'vs/base/parts/quickopen/common/quickOpen'; @@ -137,7 +137,7 @@ class CommandsHistory extends Disposable { export class ShowAllCommandsAction extends Action { static readonly ID = 'workbench.action.showCommands'; - static readonly LABEL = nls.localize('showTriggerActions', "Show All Commands"); + static readonly LABEL = localize('showTriggerActions', "Show All Commands"); constructor( id: string, @@ -167,7 +167,7 @@ export class ShowAllCommandsAction extends Action { export class ClearCommandHistoryAction extends Action { static readonly ID = 'workbench.action.clearCommandHistory'; - static readonly LABEL = nls.localize('clearCommandHistory', "Clear Command History"); + static readonly LABEL = localize('clearCommandHistory', "Clear Command History"); constructor( id: string, @@ -196,7 +196,7 @@ class CommandPaletteEditorAction extends EditorAction { constructor() { super({ id: ShowAllCommandsAction.ID, - label: nls.localize('showCommands.label', "Command Palette..."), + label: localize('showCommands.label', "Command Palette..."), alias: 'Command Palette', precondition: undefined, menuOpts: { @@ -224,10 +224,10 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { constructor( private commandId: string, - private keybinding: ResolvedKeybinding, + private keybinding: ResolvedKeybinding | undefined, private label: string, - alias: string, - highlights: { label: IHighlight[], alias?: IHighlight[] }, + alias: string | undefined, + highlights: { label: IHighlight[] | null, alias: IHighlight[] | null }, private onBeforeRun: (commandId: string) => void, @INotificationService private readonly notificationService: INotificationService, @ITelemetryService protected telemetryService: ITelemetryService @@ -240,10 +240,10 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { if (this.label !== alias) { this.alias = alias; } else { - highlights.alias = undefined; + highlights.alias = null; } - this.setHighlights(highlights.label, undefined, highlights.alias); + this.setHighlights(withNullAsUndefined(highlights.label), undefined, withNullAsUndefined(highlights.alias)); } getCommandId(): string { @@ -266,7 +266,7 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { this.description = description; } - getKeybinding(): ResolvedKeybinding { + getKeybinding(): ResolvedKeybinding | undefined { return this.keybinding; } @@ -276,10 +276,10 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { getAriaLabel(): string { if (this.keybindingAriaLabel) { - return nls.localize('entryAriaLabelWithKey', "{0}, {1}, commands", this.getLabel(), this.keybindingAriaLabel); + return localize('entryAriaLabelWithKey', "{0}, {1}, commands", this.getLabel(), this.keybindingAriaLabel); } - return nls.localize('entryAriaLabel', "{0}, commands", this.getLabel()); + return localize('entryAriaLabel', "{0}, commands", this.getLabel()); } run(mode: Mode, context: IEntryRunContext): boolean { @@ -319,7 +319,7 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { this.onError(error); } } else { - this.notificationService.info(nls.localize('actionNotEnabled', "Command '{0}' is not enabled in the current context.", this.getLabel())); + this.notificationService.info(localize('actionNotEnabled', "Command '{0}' is not enabled in the current context.", this.getLabel())); } }, 50); } @@ -329,7 +329,7 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { return; } - this.notificationService.error(error || nls.localize('canNotRun', "Command '{0}' resulted in an error.", this.label)); + this.notificationService.error(error || localize('canNotRun', "Command '{0}' resulted in an error.", this.label)); } } @@ -337,10 +337,10 @@ class EditorActionCommandEntry extends BaseCommandEntry { constructor( commandId: string, - keybinding: ResolvedKeybinding, + keybinding: ResolvedKeybinding | undefined, label: string, - meta: string, - highlights: { label: IHighlight[], alias: IHighlight[] }, + meta: string | undefined, + highlights: { label: IHighlight[] | null, alias: IHighlight[] | null }, private action: IEditorAction, onBeforeRun: (commandId: string) => void, @INotificationService notificationService: INotificationService, @@ -358,10 +358,10 @@ class ActionCommandEntry extends BaseCommandEntry { constructor( commandId: string, - keybinding: ResolvedKeybinding, + keybinding: ResolvedKeybinding | undefined, label: string, - alias: string, - highlights: { label: IHighlight[], alias: IHighlight[] }, + alias: string | undefined, + highlights: { label: IHighlight[] | null, alias: IHighlight[] | null }, private action: Action, onBeforeRun: (commandId: string) => void, @INotificationService notificationService: INotificationService, @@ -439,7 +439,7 @@ export class CommandsHandler extends QuickOpenHandler implements IDisposable { // Editor Actions const activeTextEditorWidget = this.editorService.activeTextEditorWidget; let editorActions: IEditorAction[] = []; - if (activeTextEditorWidget && types.isFunction(activeTextEditorWidget.getSupportedActions)) { + if (activeTextEditorWidget && isFunction(activeTextEditorWidget.getSupportedActions)) { editorActions = activeTextEditorWidget.getSupportedActions(); } @@ -456,7 +456,7 @@ export class CommandsHandler extends QuickOpenHandler implements IDisposable { let entries = [...editorEntries, ...commandEntries]; // Remove duplicates - entries = arrays.distinct(entries, entry => `${entry.getLabel()}${entry.getGroupLabel()}${entry.getCommandId()}`); + entries = distinct(entries, entry => `${entry.getLabel()}${entry.getGroupLabel()}${entry.getCommandId()}`); // Handle label clashes const commandLabels = new Set(); @@ -494,12 +494,12 @@ export class CommandsHandler extends QuickOpenHandler implements IDisposable { // only if we have recently used commands in the result set const firstEntry = entries[0]; if (firstEntry && this.commandsHistory.peek(firstEntry.getCommandId())) { - firstEntry.setGroupLabel(nls.localize('recentlyUsed', "recently used")); + firstEntry.setGroupLabel(localize('recentlyUsed', "recently used")); for (let i = 1; i < entries.length; i++) { const entry = entries[i]; if (!this.commandsHistory.peek(entry.getCommandId())) { entry.setShowBorder(true); - entry.setGroupLabel(nls.localize('morecCommands', "other commands")); + entry.setGroupLabel(localize('morecCommands', "other commands")); break; } } @@ -520,7 +520,7 @@ export class CommandsHandler extends QuickOpenHandler implements IDisposable { if (label) { // Alias for non default languages - const alias = !Language.isDefaultVariant() ? action.alias : null; + const alias = !Language.isDefaultVariant() ? action.alias : undefined; const labelHighlights = wordFilter(searchValue, label); const aliasHighlights = alias ? wordFilter(searchValue, alias) : null; @@ -547,15 +547,15 @@ export class CommandsHandler extends QuickOpenHandler implements IDisposable { let category, label = title; if (action.item.category) { category = typeof action.item.category === 'string' ? action.item.category : action.item.category.value; - label = nls.localize('cat.title', "{0}: {1}", category, title); + label = localize('cat.title', "{0}: {1}", category, title); } if (label) { const labelHighlights = wordFilter(searchValue, label); // Add an 'alias' in original language when running in different locale - const aliasTitle = (!Language.isDefaultVariant() && typeof action.item.title !== 'string') ? action.item.title.original : null; - const aliasCategory = (!Language.isDefaultVariant() && category && action.item.category && typeof action.item.category !== 'string') ? action.item.category.original : null; + const aliasTitle = (!Language.isDefaultVariant() && typeof action.item.title !== 'string') ? action.item.title.original : undefined; + const aliasCategory = (!Language.isDefaultVariant() && category && action.item.category && typeof action.item.category !== 'string') ? action.item.category.original : undefined; let alias; if (aliasTitle && category) { alias = aliasCategory ? `${aliasCategory}: ${aliasTitle}` : `${category}: ${aliasTitle}`; @@ -590,7 +590,7 @@ export class CommandsHandler extends QuickOpenHandler implements IDisposable { } getEmptyLabel(searchString: string): string { - return nls.localize('noCommandsMatching', "No commands matching"); + return localize('noCommandsMatching', "No commands matching"); } onClose(canceled: boolean): void { diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts index 60a0a3da383..16ef00ee8b7 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts @@ -16,7 +16,7 @@ import { IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecorati import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen'; -import { DocumentSymbolProviderRegistry, DocumentSymbol, symbolKindToCssClass, SymbolKind, SymbolTag } from 'vs/editor/common/modes'; +import { DocumentSymbolProviderRegistry, DocumentSymbol, SymbolKinds, SymbolKind, SymbolTag } from 'vs/editor/common/modes'; import { IRange, Range } from 'vs/editor/common/core/range'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry'; @@ -195,7 +195,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { return this.deprecated ? { extraClasses: ['deprecated'] } : undefined; } - getHighlights(): [IHighlight[], IHighlight[] | undefined, IHighlight[] | undefined] { + getHighlights(): [IHighlight[] | undefined, IHighlight[] | undefined, IHighlight[] | undefined] { return [ this.deprecated ? [] : filters.createMatches(this.score), undefined, @@ -425,7 +425,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { // Show parent scope as description const description = element.containerName || ''; - const icon = symbolKindToCssClass(element.kind); + const icon = SymbolKinds.toCssClassName(element.kind); // Add results.push(new SymbolEntry(i, diff --git a/src/vs/workbench/contrib/quickopen/browser/helpHandler.ts b/src/vs/workbench/contrib/quickopen/browser/helpHandler.ts index ac714337b35..8d8aaafcca2 100644 --- a/src/vs/workbench/contrib/quickopen/browser/helpHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/helpHandler.ts @@ -17,11 +17,11 @@ export const HELP_PREFIX = '?'; class HelpEntry extends QuickOpenEntryGroup { private prefixLabel: string; private prefix: string; - private description: string; + private description: string | undefined; private quickOpenService: IQuickOpenService; private openOnPreview: boolean; - constructor(prefix: string, description: string, quickOpenService: IQuickOpenService, openOnPreview: boolean) { + constructor(prefix: string, description: string | undefined, openOnPreview: boolean, quickOpenService: IQuickOpenService) { super(); if (!prefix) { @@ -44,7 +44,7 @@ class HelpEntry extends QuickOpenEntryGroup { return nls.localize('entryAriaLabel', "{0}, picker help", this.getLabel()); } - getDescription(): string { + getDescription(): string | undefined { return this.description; } @@ -101,9 +101,9 @@ export class HelpHandler extends QuickOpenHandler { matchingHandlers.forEach(handler => { if (handler instanceof QuickOpenHandlerDescriptor) { - workbenchScoped.push(new HelpEntry(handler.prefix, handler.description, this.quickOpenService, matchingHandlers.length === 1)); + workbenchScoped.push(new HelpEntry(handler.prefix, handler.description, matchingHandlers.length === 1, this.quickOpenService)); } else { - const entry = new HelpEntry(handler.prefix, handler.description, this.quickOpenService, matchingHandlers.length === 1); + const entry = new HelpEntry(handler.prefix, handler.description, matchingHandlers.length === 1, this.quickOpenService); if (handler.needsEditor) { editorScoped.push(entry); } else { diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 7384ae902f0..9f01d0a2445 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -26,6 +26,7 @@ interface IConfiguration extends IWindowsConfiguration { telemetry: { enableCrashReporter: boolean }; workbench: { list: { horizontalScrolling: boolean } }; debug: { console: { wordWrap: boolean } }; + configurationSync: { enableAuth: boolean }; } export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { @@ -38,6 +39,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private enableCrashReporter: boolean | undefined; private treeHorizontalScrolling: boolean | undefined; private debugConsoleWordWrap: boolean | undefined; + private enableConfigSyncAuth: boolean | undefined; constructor( @IHostService private readonly hostService: IHostService, @@ -105,6 +107,12 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo } } + // Configuration Sync Auth + if (config.configurationSync && typeof config.configurationSync.enableAuth === 'boolean' && config.configurationSync.enableAuth !== this.enableConfigSyncAuth) { + this.enableConfigSyncAuth = config.configurationSync.enableAuth; + changed = true; + } + // Notify only when changed and we are the focused window (avoids notification spam across windows) if (notify && changed) { this.doConfirm( diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index ed0a250c322..bc4db1d2a9f 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -433,8 +433,10 @@ export class RemoteViewlet extends ViewContainerViewlet implements IViewModel { } const descriptorAuthority = descriptor.viewDescriptor.remoteAuthority; - if (typeof descriptorAuthority === 'undefined' || descriptor.viewDescriptor.id === HelpPanel.ID) { + if (typeof descriptorAuthority === 'undefined') { panel.setExpanded(true); + } else if (descriptor.viewDescriptor.id === HelpPanel.ID) { + // Do nothing, keep the default behavior for Help } else { const descriptorAuthorityArr = Array.isArray(descriptorAuthority) ? descriptorAuthority : [descriptorAuthority]; if (descriptorAuthorityArr.indexOf(actualRemoteAuthority) >= 0) { @@ -466,7 +468,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie class OpenRemoteViewletAction extends ShowViewletAction { static readonly ID = VIEWLET_ID; - static LABEL = nls.localize('toggleRemoteViewlet', "Show Remote Explorer"); + static readonly LABEL = nls.localize('toggleRemoteViewlet', "Show Remote Explorer"); constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService) { super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService); diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index c7aec31036f..b639e3fbd74 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -17,7 +17,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; -import { ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMService, ISCMRepository, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { registerThemingParticipant, ITheme, ICssStyleCollector, themeColorFromId, IThemeService } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; @@ -37,7 +37,7 @@ import { IDiffEditorOptions, EditorOption } from 'vs/editor/common/config/editor import { Action, IAction, ActionRunner } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { basename } from 'vs/base/common/resources'; +import { basename, isEqualOrParent } from 'vs/base/common/resources'; import { MenuId, IMenuService, IMenu, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; import { createAndFillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IChange, IEditorModel, ScrollType, IEditorContribution, IDiffEditorModel } from 'vs/editor/common/editorCommon'; @@ -163,11 +163,11 @@ function getOuterEditorFromDiffEditor(accessor: ServicesAccessor): ICodeEditor | class DirtyDiffWidget extends PeekViewWidget { - private diffEditor: EmbeddedDiffEditorWidget; + private diffEditor!: EmbeddedDiffEditorWidget; private title: string; private menu: IMenu; - private index: number; - private change: IChange; + private index: number = 0; + private change: IChange | undefined; private height: number | undefined = undefined; private contextKeyService: IContextKeyService; @@ -320,7 +320,7 @@ class DirtyDiffWidget extends PeekViewWidget { super._doLayoutBody(height, width); this.diffEditor.layout({ height, width }); - if (typeof this.height === 'undefined') { + if (typeof this.height === 'undefined' && this.change) { this.revealChange(this.change); } @@ -567,7 +567,7 @@ export class DirtyDiffController extends Disposable implements IEditorContributi private model: DirtyDiffModel | null = null; private widget: DirtyDiffWidget | null = null; private currentIndex: number = -1; - private readonly isDirtyDiffVisible: IContextKey; + private readonly isDirtyDiffVisible!: IContextKey; private session: IDisposable = Disposable.None; private mouseDownInfo: { lineNumber: number } | null = null; private enabled = false; @@ -951,9 +951,26 @@ function compareChanges(a: IChange, b: IChange): number { return a.originalEndLineNumber - b.originalEndLineNumber; } +function createProviderComparer(uri: URI): (a: ISCMProvider, b: ISCMProvider) => number { + return (a, b) => { + const aIsParent = isEqualOrParent(uri, a.rootUri!); + const bIsParent = isEqualOrParent(uri, b.rootUri!); + + if (aIsParent && bIsParent) { + return a.rootUri!.fsPath.length - b.rootUri!.fsPath.length; + } else if (aIsParent) { + return -1; + } else if (bIsParent) { + return 1; + } else { + return 0; + } + }; +} + export class DirtyDiffModel extends Disposable { - private _originalModel: ITextModel | null; + private _originalModel: ITextModel | null = null; get original(): ITextModel | null { return this._originalModel; } get modified(): ITextModel | null { return this._editorModel; } @@ -1082,13 +1099,25 @@ export class DirtyDiffModel extends Disposable { }); } - private getOriginalResource(): Promise { + private async getOriginalResource(): Promise { if (!this._editorModel) { return Promise.resolve(null); } const uri = this._editorModel.uri; - return first(this.scmService.repositories.map(r => () => r.provider.getOriginalResource(uri))); + const providers = this.scmService.repositories.map(r => r.provider); + const rootedProviders = providers.filter(p => !!p.rootUri); + + rootedProviders.sort(createProviderComparer(uri)); + + const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); + + if (result) { + return result; + } + + const nonRootedProviders = providers.filter(p => !p.rootUri); + return first(nonRootedProviders.map(p => () => p.getOriginalResource(uri))); } findNextClosestChange(lineNumber: number, inclusive = true): number { diff --git a/src/vs/workbench/contrib/scm/browser/mainPanel.ts b/src/vs/workbench/contrib/scm/browser/mainPanel.ts index 134f70ce0ba..217ab0acc4b 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPanel.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPanel.ts @@ -10,7 +10,6 @@ import { basename } from 'vs/base/common/resources'; import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { append, $, toggleClass } from 'vs/base/browser/dom'; -import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; @@ -82,7 +81,7 @@ class StatusBarActionViewItem extends ActionViewItem { } updateLabel(): void { - if (this.options.label) { + if (this.options.label && this.label) { this.label.innerHTML = renderOcticons(this.getAction().label); } } @@ -173,7 +172,7 @@ export class MainPanel extends ViewletPanel { static readonly ID = 'scm.mainPanel'; static readonly TITLE = localize('scm providers', "Source Control Providers"); - private list: List; + private list!: WorkbenchList; constructor( protected viewModel: IViewModel, diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index f8d0d427d53..3aeb9e527f7 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -41,8 +41,25 @@ } .scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-item { + padding: 0 4px; overflow: hidden; text-overflow: ellipsis; + display: flex; + align-items: center; +} + +.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-label .octicon { + font-size: 14px; +} + +.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-item:last-of-type { + padding-right: 0; +} + +.scm-viewlet .scm-provider > .name, +.scm-viewlet .scm-provider > .count { + display: flex; + align-items: center; } .scm-viewlet .scm-provider > .count { @@ -106,6 +123,7 @@ height: 100%; background-repeat: no-repeat; background-position: 50% 50%; + margin-right: 8px; } .scm-viewlet .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { @@ -170,3 +188,7 @@ width: 8px !important; margin-right: 0 !important; } + +.scm-viewlet .scm-status.show-file-icons.hide-arrows.tree-view-mode .monaco-tl-indent .indent-guide:first-child { + border: none; +} diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts b/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts index 36cc3b7d8d2..ab56475c074 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts @@ -6,12 +6,12 @@ import 'vs/css!./media/scmViewlet'; import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { basename } from 'vs/base/common/resources'; +import { basename, isEqual } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { append, $, addClass, toggleClass, trackFocus, removeClass } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -32,11 +32,11 @@ import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { format } from 'vs/base/common/strings'; import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ThrottledDelayer } from 'vs/base/common/async'; +import { ThrottledDelayer, disposableTimeout } from 'vs/base/common/async'; import { INotificationService } from 'vs/platform/notification/common/notification'; import * as platform from 'vs/base/common/platform'; import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; -import { ISequence, ISplice } from 'vs/base/common/sequence'; +import { ISplice } from 'vs/base/common/sequence'; import { ResourceTree, IBranchNode, INode } from 'vs/base/common/resourceTree'; import { ObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree'; import { Iterator } from 'vs/base/common/iterator'; @@ -50,6 +50,8 @@ import { localize } from 'vs/nls'; import { flatten } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; type TreeElement = ISCMResourceGroup | IBranchNode | ISCMResource; @@ -63,7 +65,7 @@ interface ResourceGroupTemplate { class ResourceGroupRenderer implements ICompressibleTreeRenderer { - static TEMPLATE_ID = 'resource group'; + static readonly TEMPLATE_ID = 'resource group'; get templateId(): string { return ResourceGroupRenderer.TEMPLATE_ID; } constructor( @@ -149,7 +151,7 @@ class MultipleSelectionActionRunner extends ActionRunner { class ResourceRenderer implements ICompressibleTreeRenderer, FuzzyScore, ResourceTemplate> { - static TEMPLATE_ID = 'resource'; + static readonly TEMPLATE_ID = 'resource'; get templateId(): string { return ResourceRenderer.TEMPLATE_ID; } constructor( @@ -380,8 +382,7 @@ function asTreeElement(node: INode, incompressi return { element: node, children: Iterator.map(node.children, node => asTreeElement(node, false)), - incompressible, - collapsed: false + incompressible }; } @@ -389,8 +390,8 @@ function asTreeElement(node: INode, incompressi } const enum ViewModelMode { - List, - Tree + List = 'list', + Tree = 'tree' } class ViewModel { @@ -400,7 +401,24 @@ class ViewModel { get mode(): ViewModelMode { return this._mode; } set mode(mode: ViewModelMode) { + mode = this.provider.treeRendering ? mode : ViewModelMode.List; + + if (mode === this._mode) { + return; + } + this._mode = mode; + + for (const item of this.items) { + item.tree.clear(); + + if (mode === ViewModelMode.Tree) { + for (const resource of item.resources) { + item.tree.add(resource.sourceUri, resource); + } + } + } + this.refresh(); this._onDidChangeMode.fire(mode); } @@ -408,12 +426,15 @@ class ViewModel { private items: IGroupItem[] = []; private visibilityDisposables = new DisposableStore(); private scrollTop: number | undefined; + private firstVisible = true; private disposables = new DisposableStore(); constructor( - private groups: ISequence, + private provider: ISCMProvider, private tree: ObjectTree, - private _mode: ViewModelMode + private _mode: ViewModelMode, + @IEditorService protected editorService: IEditorService, + @IConfigurationService protected configurationService: IConfigurationService, ) { } private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice): void { @@ -427,10 +448,12 @@ class ViewModel { group.onDidSplice(splice => this.onDidSpliceGroup(item, splice)) ); - const item = { group, resources, tree, disposable }; + const item: IGroupItem = { group, resources, tree, disposable }; - for (const resource of resources) { - item.tree.add(resource.sourceUri, resource); + if (this._mode === ViewModelMode.Tree) { + for (const resource of resources) { + item.tree.add(resource.sourceUri, resource); + } } itemsToInsert.push(item); @@ -446,14 +469,18 @@ class ViewModel { } private onDidSpliceGroup(item: IGroupItem, { start, deleteCount, toInsert }: ISplice): void { - for (const resource of toInsert) { - item.tree.add(resource.sourceUri, resource); + if (this._mode === ViewModelMode.Tree) { + for (const resource of toInsert) { + item.tree.add(resource.sourceUri, resource); + } } const deleted = item.resources.splice(start, deleteCount, ...toInsert); - for (const resource of deleted) { - item.tree.delete(resource.sourceUri); + if (this._mode === ViewModelMode.Tree) { + for (const resource of deleted) { + item.tree.delete(resource.sourceUri); + } } this.refresh(item); @@ -462,13 +489,16 @@ class ViewModel { setVisible(visible: boolean): void { if (visible) { this.visibilityDisposables = new DisposableStore(); - this.groups.onDidSplice(this.onDidSpliceGroups, this, this.visibilityDisposables); - this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.groups.elements }); + this.provider.groups.onDidSplice(this.onDidSpliceGroups, this, this.visibilityDisposables); + this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.provider.groups.elements }); if (typeof this.scrollTop === 'number') { this.tree.scrollTop = this.scrollTop; this.scrollTop = undefined; } + + this.editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.visibilityDisposables); + this.onDidActiveEditorChange(); } else { this.visibilityDisposables.dispose(); this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: [] }); @@ -484,6 +514,45 @@ class ViewModel { } } + private onDidActiveEditorChange(): void { + if (!this.configurationService.getValue('scm.autoReveal')) { + return; + } + + if (this.firstVisible) { + this.firstVisible = false; + this.visibilityDisposables.add(disposableTimeout(() => this.onDidActiveEditorChange(), 250)); + return; + } + + const editor = this.editorService.activeEditor; + + if (!editor) { + return; + } + + const uri = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + + if (!uri) { + return; + } + + // go backwards from last group + for (let i = this.provider.groups.elements.length - 1; i >= 0; i--) { + const group = this.provider.groups.elements[i]; + + for (const resource of group.elements) { + if (isEqual(uri, resource.sourceUri)) { + this.tree.reveal(resource); + this.tree.setSelection([resource]); + this.tree.setFocus([resource]); + + return; + } + } + } + } + dispose(): void { this.visibilityDisposables.dispose(); this.disposables.dispose(); @@ -524,12 +593,12 @@ export class RepositoryPanel extends ViewletPanel { private cachedHeight: number | undefined = undefined; private cachedWidth: number | undefined = undefined; - private inputBoxContainer: HTMLElement; - private inputBox: InputBox; - private listContainer: HTMLElement; - private tree: ObjectTree; - private viewModel: ViewModel; - private listLabels: ResourceLabels; + private inputBoxContainer!: HTMLElement; + private inputBox!: InputBox; + private listContainer!: HTMLElement; + private tree!: ObjectTree; + private viewModel!: ViewModel; + private listLabels!: ResourceLabels; private menus: SCMMenus; private toggleViewModelModeAction: ToggleViewModeAction | undefined; protected contextKeyService: IContextKeyService; @@ -547,7 +616,8 @@ export class RepositoryPanel extends ViewletPanel { @IInstantiationService protected instantiationService: IInstantiationService, @IConfigurationService protected configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @IMenuService protected menuService: IMenuService + @IMenuService protected menuService: IMenuService, + @IStorageService private storageService: IStorageService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService); @@ -694,31 +764,63 @@ export class RepositoryPanel extends ViewletPanel { this._register(this.tree.onContextMenu(this.onListContextMenu, this)); this._register(this.tree); - const mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; - this.viewModel = new ViewModel(this.repository.provider.groups, this.tree, mode); + let mode: ViewModelMode; + + if (!this.repository.provider.treeRendering) { + mode = ViewModelMode.List; + } else { + mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; + + const rootUri = this.repository.provider.rootUri; + + if (typeof rootUri !== 'undefined') { + const storageMode = this.storageService.get(`scm.repository.viewMode:${rootUri.toString()}`, StorageScope.WORKSPACE) as ViewModelMode; + + if (typeof storageMode === 'string') { + mode = storageMode; + } + } + } + + this.viewModel = this.instantiationService.createInstance(ViewModel, this.repository.provider, this.tree, mode); this._register(this.viewModel); addClass(this.listContainer, 'file-icon-themable-tree'); addClass(this.listContainer, 'show-file-icons'); - const updateIndentStyles = (theme: IFileIconTheme) => { - toggleClass(this.listContainer, 'list-view-mode', this.viewModel.mode === ViewModelMode.List); - toggleClass(this.listContainer, 'align-icons-and-twisties', this.viewModel.mode === ViewModelMode.Tree && theme.hasFileIcons && !theme.hasFolderIcons); - toggleClass(this.listContainer, 'hide-arrows', this.viewModel.mode === ViewModelMode.Tree && theme.hidesExplorerArrows === true); - }; + this.updateIndentStyles(this.themeService.getFileIconTheme()); + this._register(this.themeService.onDidFileIconThemeChange(this.updateIndentStyles, this)); + this._register(this.viewModel.onDidChangeMode(this.onDidChangeMode, this)); - updateIndentStyles(this.themeService.getFileIconTheme()); - this._register(this.themeService.onDidFileIconThemeChange(updateIndentStyles)); - this._register(this.viewModel.onDidChangeMode(() => updateIndentStyles(this.themeService.getFileIconTheme()))); - - this.toggleViewModelModeAction = new ToggleViewModeAction(this.viewModel); - this._register(this.toggleViewModelModeAction); + if (this.repository.provider.treeRendering) { + this.toggleViewModelModeAction = new ToggleViewModeAction(this.viewModel); + this._register(this.toggleViewModelModeAction); + } this._register(this.onDidChangeBodyVisibility(this._onDidChangeVisibility, this)); this.updateActions(); } + private updateIndentStyles(theme: IFileIconTheme): void { + toggleClass(this.listContainer, 'list-view-mode', this.viewModel.mode === ViewModelMode.List); + toggleClass(this.listContainer, 'tree-view-mode', this.viewModel.mode === ViewModelMode.Tree); + toggleClass(this.listContainer, 'align-icons-and-twisties', this.viewModel.mode === ViewModelMode.Tree && theme.hasFileIcons && !theme.hasFolderIcons); + toggleClass(this.listContainer, 'hide-arrows', this.viewModel.mode === ViewModelMode.Tree && theme.hidesExplorerArrows === true); + } + + private onDidChangeMode(): void { + this.updateIndentStyles(this.themeService.getFileIconTheme()); + + const rootUri = this.repository.provider.rootUri; + + if (typeof rootUri === 'undefined') { + return; + } + + this.storageService.store(`scm.repository.viewMode:${rootUri.toString()}`, this.viewModel.mode, StorageScope.WORKSPACE); + } + layoutBody(height: number | undefined = this.cachedHeight, width: number | undefined = this.cachedWidth): void { if (height === undefined) { return; diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 291d9386b1f..64a34245284 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -28,7 +28,7 @@ import { SCMService } from 'vs/workbench/contrib/scm/common/scmService'; class OpenSCMViewletAction extends ShowViewletAction { static readonly ID = VIEWLET_ID; - static LABEL = localize('toggleGitViewlet', "Show Git"); + static readonly LABEL = localize('toggleGitViewlet', "Show Git"); constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService) { super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService); @@ -114,8 +114,13 @@ Registry.as(ConfigurationExtensions.Configuration).regis localize('scm.defaultViewMode.list', "Show the repository changes as a list.") ], description: localize('scm.defaultViewMode', "Controls the default Source Control repository view mode."), - default: 'tree' - } + default: 'list' + }, + 'scm.autoReveal': { + type: 'boolean', + description: localize('autoReveal', "Controls whether the SCM view should automatically reveal and select files when opening them."), + default: true + }, } }); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 4ce397b202f..a4207212fd4 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -54,7 +54,7 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { private static readonly STATE_KEY = 'workbench.scm.views.state'; - private el: HTMLElement; + private el!: HTMLElement; private message: HTMLElement; private menus: SCMMenus; private _repositories: ISCMRepository[] = []; diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 106a85c5f81..de4979ae7d9 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -62,6 +62,7 @@ export interface ISCMProvider extends IDisposable { readonly acceptInputCommand?: Command; readonly statusBarCommands?: Command[]; readonly onDidChange: Event; + readonly treeRendering: boolean; getOriginalResource(uri: URI): Promise; } diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index 02cc969b0d6..2ddcf6be133 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -91,8 +91,12 @@ .search-view .search-widget .replace-container .monaco-action-bar .action-item .codicon { background-repeat: no-repeat; - width: 20px; + width: 25px; height: 25px; + margin-right: 0; + display: flex; + align-items: center; + justify-content: center; } .search-view .query-details { @@ -103,10 +107,9 @@ .search-view .query-details .more { position: absolute; - margin-right: 0.3em; right: 0; cursor: pointer; - width: 16px; + width: 25px; height: 16px; z-index: 2; /* Force it above the search results message, which has a negative top margin */ } diff --git a/src/vs/workbench/contrib/search/browser/openFileHandler.ts b/src/vs/workbench/contrib/search/browser/openFileHandler.ts index 0374b69749f..c2ecf876664 100644 --- a/src/vs/workbench/contrib/search/browser/openFileHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openFileHandler.ts @@ -50,7 +50,7 @@ export class FileEntry extends EditorQuickOpenEntry { private resource: URI, private name: string, private description: string, - private icon: string, + private icon: string | undefined, @IEditorService editorService: IEditorService, @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService, @@ -78,7 +78,7 @@ export class FileEntry extends EditorQuickOpenEntry { return this.description; } - getIcon(): string { + getIcon(): string | undefined { return this.icon; } diff --git a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts index c0bcedcb221..2354211e6ad 100644 --- a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts @@ -14,7 +14,7 @@ import * as filters from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; -import { symbolKindToCssClass, SymbolTag } from 'vs/editor/common/modes'; +import { SymbolKinds, SymbolTag } from 'vs/editor/common/modes'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -47,7 +47,7 @@ class SymbolEntry extends EditorQuickOpenEntry { this.score = score; } - getHighlights(): [IHighlight[] /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { + getHighlights(): [IHighlight[] | undefined /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { return [this.isDeprecated() ? [] : filters.createMatches(this.score), undefined, undefined]; } @@ -73,7 +73,7 @@ class SymbolEntry extends EditorQuickOpenEntry { } getIcon(): string { - return symbolKindToCssClass(this.bearing.kind); + return SymbolKinds.toCssClassName(this.bearing.kind); } getLabelOptions(): IIconLabelValueOptions | undefined { @@ -145,8 +145,8 @@ class SymbolEntry extends EditorQuickOpenEntry { if (res !== 0) { return res; } - let aKind = symbolKindToCssClass(a.bearing.kind); - let bKind = symbolKindToCssClass(b.bearing.kind); + let aKind = SymbolKinds.toCssClassName(a.bearing.kind); + let bKind = SymbolKinds.toCssClassName(b.bearing.kind); return aKind.localeCompare(bKind); } } diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 502e32a326c..bc7e14ab2e2 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -130,7 +130,7 @@ export class ReplaceService implements IReplaceService { this.updateReplacePreview(fileMatch).then(() => { if (editor) { const editorControl = editor.getControl(); - if (element instanceof Match) { + if (element instanceof Match && editorControl) { editorControl.revealLineInCenter(element.range().startLineNumber, ScrollType.Immediate); } } diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 099312d3d98..c1dc5804ba3 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -494,7 +494,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action { export class RemoveAction extends AbstractSearchAndReplaceAction { - static LABEL = nls.localize('RemoveAction.label', "Dismiss"); + static readonly LABEL = nls.localize('RemoveAction.label', "Dismiss"); constructor( private viewer: WorkbenchObjectTree, diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index d9bdee92201..326e78344bb 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -930,6 +930,8 @@ export class SearchView extends ViewletPanel { this.searchWidget.clear(); this.viewModel.cancelSearch(); this.updateActions(); + + aria.status(nls.localize('ariaSearchResultsClearStatus', "The search results have been cleared")); } cancelSearch(): boolean { diff --git a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts index 20f06e31a9e..9522f3006b1 100644 --- a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts +++ b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts @@ -24,7 +24,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; export class TabCompletionController implements editorCommon.IEditorContribution { private static readonly ID = 'editor.tabCompletionController'; - static ContextKey = new RawContextKey('hasSnippetCompletions', undefined); + static readonly ContextKey = new RawContextKey('hasSnippetCompletions', undefined); public static get(editor: ICodeEditor): TabCompletionController { return editor.getContribution(TabCompletionController.ID); diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index 657096ea265..69b99fd7c0b 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -25,6 +25,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import * as perf from 'vs/base/common/performance'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; +import { assertIsDefined } from 'vs/base/common/types'; class PartsSplash { @@ -81,10 +82,10 @@ class PartsSplash { const layoutInfo = !this._shouldSaveLayoutInfo() ? undefined : { sideBarSide: this._layoutService.getSideBarPosition() === Position.RIGHT ? 'right' : 'left', editorPartMinWidth: DEFAULT_EDITOR_MIN_DIMENSIONS.width, - titleBarHeight: this._layoutService.isVisible(Parts.TITLEBAR_PART) ? getTotalHeight(this._layoutService.getContainer(Parts.TITLEBAR_PART)) : 0, - activityBarWidth: this._layoutService.isVisible(Parts.ACTIVITYBAR_PART) ? getTotalWidth(this._layoutService.getContainer(Parts.ACTIVITYBAR_PART)) : 0, - sideBarWidth: this._layoutService.isVisible(Parts.SIDEBAR_PART) ? getTotalWidth(this._layoutService.getContainer(Parts.SIDEBAR_PART)) : 0, - statusBarHeight: this._layoutService.isVisible(Parts.STATUSBAR_PART) ? getTotalHeight(this._layoutService.getContainer(Parts.STATUSBAR_PART)) : 0, + titleBarHeight: this._layoutService.isVisible(Parts.TITLEBAR_PART) ? getTotalHeight(assertIsDefined(this._layoutService.getContainer(Parts.TITLEBAR_PART))) : 0, + activityBarWidth: this._layoutService.isVisible(Parts.ACTIVITYBAR_PART) ? getTotalWidth(assertIsDefined(this._layoutService.getContainer(Parts.ACTIVITYBAR_PART))) : 0, + sideBarWidth: this._layoutService.isVisible(Parts.SIDEBAR_PART) ? getTotalWidth(assertIsDefined(this._layoutService.getContainer(Parts.SIDEBAR_PART))) : 0, + statusBarHeight: this._layoutService.isVisible(Parts.STATUSBAR_PART) ? getTotalHeight(assertIsDefined(this._layoutService.getContainer(Parts.STATUSBAR_PART))) : 0, }; this._textFileService.write( URI.file(join(this._envService.userDataPath, 'rapid_render.json')), diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 24306cab559..70a8b49af1a 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -77,6 +77,7 @@ import { applyEdits } from 'vs/base/common/jsonEdit'; import { ITextEditor } from 'vs/workbench/common/editor'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { find } from 'vs/base/common/arrays'; export namespace ConfigureTaskAction { export const ID = 'workbench.action.tasks.configureTaskRunner'; @@ -290,7 +291,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer let entry: TaskQuickPickEntry | null | undefined; if (tasks && tasks.length > 0) { - entry = await this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task')); + entry = await this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task (there is no default build task defined)')); } let task: Task | undefined | null = entry ? entry.task : undefined; @@ -371,8 +372,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.runConfigureDefaultTestTask(); }); - CommandsRegistry.registerCommand('workbench.action.tasks.showTasks', () => { - this.runShowTasks(); + CommandsRegistry.registerCommand('workbench.action.tasks.showTasks', async () => { + return this.runShowTasks(); }); CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => { @@ -515,12 +516,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!values) { return undefined; } - for (const task of values) { - if (task.matches(key, compareId)) { - return task; - } - } - return undefined; + return find(values, task => task.matches(key, compareId)); }); } @@ -573,6 +569,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return Promise.resolve(this._taskSystem.getActiveTasks()); } + public getBusyTasks(): Promise { + if (!this._taskSystem) { + return Promise.resolve([]); + } + return Promise.resolve(this._taskSystem.getBusyTasks()); + } + public getRecentlyUsedTasks(): LinkedMap { if (this._recentlyUsedTasks) { return this._recentlyUsedTasks; @@ -677,7 +680,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } + private isProvideTasksEnabled(): boolean { + const settingValue = this.configurationService.getValue('task.autoDetect'); + return settingValue === true; + } + private shouldAttachProblemMatcher(task: Task): boolean { + const settingValue = this.configurationService.getValue('task.problemMatchers.neverPrompt'); + if (settingValue === true) { + return false; + } else if (task.type && Types.isStringArray(settingValue) && (settingValue.indexOf(task.type) >= 0)) { + return false; + } if (!this.canCustomize(task)) { return false; } @@ -983,7 +997,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer private getResourceForTask(task: CustomTask): URI { let uri = this.getResourceForKind(task._source.kind); if (!uri) { - uri = task.getWorkspaceFolder().toResource(task._source.config.file); + const taskFolder = task.getWorkspaceFolder(); + if (taskFolder) { + uri = taskFolder.toResource(task._source.config.file); + } else { + uri = this.workspaceFolders[0].uri; + } } return uri; } @@ -1245,7 +1264,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } }; - if (this.schemaVersion === JsonSchemaVersion.V2_0_0 && this._providers.size > 0) { + if (this.isProvideTasksEnabled() && (this.schemaVersion === JsonSchemaVersion.V2_0_0) && (this._providers.size > 0)) { for (const [handle, provider] of this._providers) { if ((type === undefined) || (type === this._providerTypes.get(handle))) { counter++; @@ -2474,23 +2493,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - public runShowTasks(): void { + public async runShowTasks(): Promise { if (!this.canRunCommand()) { return; } - this.showQuickPick(this.getActiveTasks(), - nls.localize('TaskService.pickShowTask', 'Select the task to show its output'), - { - label: nls.localize('TaskService.noTaskIsRunning', 'No task is running'), - task: null - }, - false, true - ).then((entry) => { - let task: Task | undefined | null = entry ? entry.task : undefined; - if (task === undefined || task === null) { - return; - } - this._taskSystem!.revealTask(task); - }); + const activeTasks: Task[] = await this.getActiveTasks(); + if (activeTasks.length === 1) { + this._taskSystem!.revealTask(activeTasks[0]); + } else { + this.showQuickPick(this.getActiveTasks(), + nls.localize('TaskService.pickShowTask', 'Select the task to show its output'), + { + label: nls.localize('TaskService.noTaskIsRunning', 'No task is running'), + task: null + }, + false, true + ).then((entry) => { + let task: Task | undefined | null = entry ? entry.task : undefined; + if (task === undefined || task === null) { + return; + } + this._taskSystem!.revealTask(task); + }); + } } } diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 23ecbb6684a..96136e89d0b 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -35,6 +35,11 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor import { RunAutomaticTasks, ManageAutomaticTaskRunning } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import schemaVersion1 from '../common/jsonSchema_v1'; +import schemaVersion2, { updateProblemMatchers } from '../common/jsonSchema_v2'; +import { AbstractTaskService, ConfigureTaskAction } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; +import { tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; let tasksCategory = nls.localize('tasksCategory', "Tasks"); @@ -288,10 +293,6 @@ let schema: IJSONSchema = { } }; -import schemaVersion1 from '../common/jsonSchema_v1'; -import schemaVersion2, { updateProblemMatchers } from '../common/jsonSchema_v2'; -import { AbstractTaskService, ConfigureTaskAction } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; -import { tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; schema.definitions = { ...schemaVersion1.definitions, ...schemaVersion2.definitions, @@ -305,3 +306,35 @@ ProblemMatcherRegistry.onMatcherChanged(() => { updateProblemMatchers(); jsonRegistry.notifySchemaChanged(tasksSchemaId); }); + +const configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + id: 'task', + order: 100, + title: nls.localize('tasksConfigurationTitle', "Tasks"), + type: 'object', + properties: { + 'task.problemMatchers.neverPrompt': { + markdownDescription: nls.localize('task.problemMatchers.neverPrompt', "Configures whether to show the problem matcher prompt when running a task. Set to `true` to never promp, or use an array of task types to turn off prompting only for specific task types."), + 'oneOf': [ + { + type: 'boolean', + markdownDescription: nls.localize('task.problemMatchers.neverPrompt.boolean', 'Sets problem matcher prompting behavior for all tasks.') + }, + { + type: 'array', + items: { + type: 'string', + markdownDescription: nls.localize('task.problemMatchers.neverPrompt.array', 'An array of task types to never prompt for problem matchers on.') + } + } + ], + default: false + }, + 'task.autoDetect': { + markdownDescription: nls.localize('task.autoDetect', "Controls enablement of `provideTasks` for all task provider extension. If the Tasks: Run Task command is slow, disabling auto detect for task providers may help. Individual extensions my provide settings to disabled auto detection."), + type: 'boolean', + default: true + } + } +}); diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index ce5593930c6..62f8180824b 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -113,7 +113,7 @@ export class TerminalTaskSystem implements ITaskSystem { public static TelemetryEventName: string = 'taskService'; - private static ProcessVarName = '__process__'; + private static readonly ProcessVarName = '__process__'; private static shellQuotes: IStringDictionary = { 'cmd': { @@ -152,6 +152,7 @@ export class TerminalTaskSystem implements ITaskSystem { }; private activeTasks: IStringDictionary; + private busyTasks: IStringDictionary; private terminals: IStringDictionary; private idleTaskTerminals: LinkedMap; private sameTaskTerminals: IStringDictionary; @@ -180,6 +181,7 @@ export class TerminalTaskSystem implements ITaskSystem { ) { this.activeTasks = Object.create(null); + this.busyTasks = Object.create(null); this.terminals = Object.create(null); this.idleTaskTerminals = new LinkedMap(); this.sameTaskTerminals = Object.create(null); @@ -280,6 +282,10 @@ export class TerminalTaskSystem implements ITaskSystem { return Object.keys(this.activeTasks).map(key => this.activeTasks[key].task); } + public getBusyTasks(): Task[] { + return Object.keys(this.busyTasks).map(key => this.busyTasks[key]); + } + public customExecutionComplete(task: Task, result: number): Promise { let activeTerminal = this.activeTasks[task.getMapKey()]; if (!activeTerminal) { @@ -462,7 +468,14 @@ export class TerminalTaskSystem implements ITaskSystem { } private executeCommand(task: CustomTask | ContributedTask, trigger: string): Promise { - const workspaceFolder = this.currentTask.workspaceFolder = task.getWorkspaceFolder(); + const taskWorkspaceFolder = task.getWorkspaceFolder(); + let workspaceFolder: IWorkspaceFolder | undefined; + if (taskWorkspaceFolder) { + workspaceFolder = this.currentTask.workspaceFolder = taskWorkspaceFolder; + } else { + const folders = this.contextService.getWorkspace().folders; + workspaceFolder = folders.length > 0 ? folders[0] : undefined; + } const systemInfo: TaskSystemInfo | undefined = this.currentTask.systemInfo = workspaceFolder ? this.taskSystemInfoResolver(workspaceFolder) : undefined; let variables = new Set(); @@ -470,11 +483,13 @@ export class TerminalTaskSystem implements ITaskSystem { const resolvedVariables = this.resolveVariablesFromSet(systemInfo, workspaceFolder, task, variables); return resolvedVariables.then((resolvedVariables) => { - const isCustomExecution = (task.command.runtime === RuntimeType.CustomExecution2); + const isCustomExecution = (task.command.runtime === RuntimeType.CustomExecution); if (resolvedVariables && (task.command !== undefined) && task.command.runtime && (isCustomExecution || (task.command.name !== undefined))) { this.currentTask.resolvedVariables = resolvedVariables; return this.executeInTerminal(task, trigger, new VariableResolver(workspaceFolder, systemInfo, resolvedVariables.variables, this.configurationResolverService), workspaceFolder); } else { + // Allows the taskExecutions array to be updated in the extension host + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); return Promise.resolve({ exitCode: 0 }); } }, reason => { @@ -520,14 +535,23 @@ export class TerminalTaskSystem implements ITaskSystem { if (task.configurationProperties.isBackground) { const problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers); let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService, this.fileService); + if ((problemMatchers.length > 0) && !watchingProblemMatcher.isWatching()) { + this.appendOutput(nls.localize('TerminalTaskSystem.nonWatchingMatcher', 'Task {0} is a background task but uses a problem matcher without a background pattern', task._label)); + this.showOutput(); + } const toDispose = new DisposableStore(); let eventCounter: number = 0; + const mapKey = task.getMapKey(); toDispose.add(watchingProblemMatcher.onDidStateChange((event) => { if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) { eventCounter++; + this.busyTasks[mapKey] = task; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task)); } else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) { eventCounter--; + if (this.busyTasks[mapKey]) { + delete this.busyTasks[mapKey]; + } this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task)); if (eventCounter === 0) { if ((watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && @@ -586,6 +610,9 @@ export class TerminalTaskSystem implements ITaskSystem { onData.dispose(); onExit.dispose(); let key = task.getMapKey(); + if (this.busyTasks[mapKey]) { + delete this.busyTasks[mapKey]; + } delete this.activeTasks[key]; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed)); if (exitCode !== undefined) { @@ -645,6 +672,8 @@ export class TerminalTaskSystem implements ITaskSystem { // The process never got ready. Need to think how to handle this. }); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id)); + const mapKey = task.getMapKey(); + this.busyTasks[mapKey] = task; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task)); let problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers); let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService, ProblemHandlingStrategy.Clean, this.fileService); @@ -698,6 +727,9 @@ export class TerminalTaskSystem implements ITaskSystem { } this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); + if (this.busyTasks[mapKey]) { + delete this.busyTasks[mapKey]; + } this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task)); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); resolve({ exitCode }); @@ -850,7 +882,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } } else { - let commandExecutable = (task.command.runtime !== RuntimeType.CustomExecution2) ? CommandString.value(command) : undefined; + let commandExecutable = (task.command.runtime !== RuntimeType.CustomExecution) ? CommandString.value(command) : undefined; let executable = !isShellCommand ? this.resolveVariable(variableResolver, '${' + TerminalTaskSystem.ProcessVarName + '}') : commandExecutable; @@ -921,7 +953,7 @@ export class TerminalTaskSystem implements ITaskSystem { let args: CommandString[] | undefined; let launchConfigs: IShellLaunchConfig | undefined; - if (task.command.runtime === RuntimeType.CustomExecution2) { + if (task.command.runtime === RuntimeType.CustomExecution) { this.currentTask.shellLaunchConfig = launchConfigs = { isExtensionTerminal: true, waitOnExit, @@ -976,6 +1008,7 @@ export class TerminalTaskSystem implements ITaskSystem { throw new Error('Task shell launch configuration should not be undefined here.'); } + terminalToReuse.terminal.scrollToBottom(); terminalToReuse.terminal.reuseTerminal(launchConfigs); if (task.command.presentation && task.command.presentation.clear) { @@ -1016,7 +1049,11 @@ export class TerminalTaskSystem implements ITaskSystem { // This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected. // For correct terminal re-use, the task needs to be deleted immediately. // Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate. - delete this.activeTasks[task.getMapKey()]; + const mapKey = task.getMapKey(); + delete this.activeTasks[mapKey]; + if (this.busyTasks[mapKey]) { + delete this.busyTasks[mapKey]; + } } }); this.terminals[terminalKey] = { terminal: result, lastTask: taskKey, group }; @@ -1142,7 +1179,7 @@ export class TerminalTaskSystem implements ITaskSystem { private collectCommandVariables(variables: Set, command: CommandConfiguration, task: CustomTask | ContributedTask): void { // The custom execution should have everything it needs already as it provided // the callback. - if (command.runtime === RuntimeType.CustomExecution2) { + if (command.runtime === RuntimeType.CustomExecution) { return; } diff --git a/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts b/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts index 89f5ef65397..0d868b77c1a 100644 --- a/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts +++ b/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts @@ -115,16 +115,52 @@ const schema: IJSONSchema = { $ref: '#/definitions/options' }, windows: { - $ref: '#/definitions/commandConfiguration', - description: nls.localize('JsonSchema.tasks.windows', 'Windows specific command configuration') + anyOf: [ + { + $ref: '#/definitions/commandConfiguration', + description: nls.localize('JsonSchema.tasks.windows', 'Windows specific command configuration'), + }, + { + properties: { + problemMatcher: { + $ref: '#/definitions/problemMatcherType', + description: nls.localize('JsonSchema.tasks.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.') + } + } + } + ] }, osx: { - $ref: '#/definitions/commandConfiguration', - description: nls.localize('JsonSchema.tasks.mac', 'Mac specific command configuration') + anyOf: [ + { + $ref: '#/definitions/commandConfiguration', + description: nls.localize('JsonSchema.tasks.mac', 'Mac specific command configuration') + }, + { + properties: { + problemMatcher: { + $ref: '#/definitions/problemMatcherType', + description: nls.localize('JsonSchema.tasks.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.') + } + } + } + ] }, linux: { - $ref: '#/definitions/commandConfiguration', - description: nls.localize('JsonSchema.tasks.linux', 'Linux specific command configuration') + anyOf: [ + { + $ref: '#/definitions/commandConfiguration', + description: nls.localize('JsonSchema.tasks.linux', 'Linux specific command configuration') + }, + { + properties: { + problemMatcher: { + $ref: '#/definitions/problemMatcherType', + description: nls.localize('JsonSchema.tasks.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.') + } + } + } + ] }, suppressTaskName: { type: 'boolean', @@ -241,4 +277,4 @@ const schema: IJSONSchema = { } }; -export default schema; \ No newline at end of file +export default schema; diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 30333ecdc3d..70e9d1b1fb5 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -524,4 +524,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement }); super.done(); } + + public isWatching(): boolean { + return this.backgroundPatterns.length > 0; + } } diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index dcefd54e051..fa7fbeb3133 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -1115,6 +1115,20 @@ namespace ProblemMatcherConverter { return result; } + export function fromWithOsConfig(this: void, external: ConfigurationProperties & { [key: string]: any; }, context: ParseContext): ProblemMatcher[] | undefined { + let result: ProblemMatcher[] | undefined = undefined; + if (external.windows && external.windows.problemMatcher && context.platform === Platform.Windows) { + result = from(external.windows.problemMatcher, context); + } else if (external.osx && external.osx.problemMatcher && context.platform === Platform.Mac) { + result = from(external.osx.problemMatcher, context); + } else if (external.linux && external.linux.problemMatcher && context.platform === Platform.Linux) { + result = from(external.linux.problemMatcher, context); + } else if (external.problemMatcher) { + result = from(external.problemMatcher, context); + } + return result; + } + export function from(this: void, config: ProblemMatcherConfig.ProblemMatcherType | undefined, context: ParseContext): ProblemMatcher[] { let result: ProblemMatcher[] = []; if (config === undefined) { @@ -1305,8 +1319,9 @@ namespace ConfigurationProperties { if (includeCommandOptions && (external.options !== undefined)) { result.options = CommandOptions.from(external.options, context); } - if (external.problemMatcher) { - result.problemMatchers = ProblemMatcherConverter.from(external.problemMatcher, context); + const configProblemMatcher = ProblemMatcherConverter.fromWithOsConfig(external, context); + if (configProblemMatcher !== undefined) { + result.problemMatchers = configProblemMatcher; } return isEmpty(result) ? undefined : result; } @@ -2047,80 +2062,3 @@ export function createCustomTask(contributedTask: Tasks.ContributedTask, configu return CustomTask.createCustomTask(contributedTask, configuredProps); } -/* -class VersionConverter { - constructor(private problemReporter: IProblemReporter) { - } - - public convert(fromConfig: ExternalTaskRunnerConfiguration): ExternalTaskRunnerConfiguration { - let result: ExternalTaskRunnerConfiguration; - result.version = '2.0.0'; - if (Array.isArray(fromConfig.tasks)) { - - } else { - result.tasks = []; - } - - - return result; - } - - private convertGlobalTask(fromConfig: ExternalTaskRunnerConfiguration): TaskDescription { - let command: string = this.getGlobalCommand(fromConfig); - if (!command) { - this.problemReporter.error(nls.localize('Converter.noGlobalName', 'No global command specified. Can\'t convert to 2.0.0 version.')); - return undefined; - } - let result: TaskDescription = { - taskName: command - }; - if (fromConfig.isShellCommand) { - result.type = 'shell'; - } else { - result.type = 'process'; - result.args = fromConfig.args; - } - if (fromConfig.) - - return result; - } - - private getGlobalCommand(fromConfig: ExternalTaskRunnerConfiguration): string { - if (fromConfig.command) { - return fromConfig.command; - } else if (fromConfig.windows && fromConfig.windows.command) { - return fromConfig.windows.command; - } else if (fromConfig.osx && fromConfig.osx.command) { - return fromConfig.osx.command; - } else if (fromConfig.linux && fromConfig.linux.command) { - return fromConfig.linux.command; - } else { - return undefined; - } - } - - private createCommandLine(command: string, args: string[], isWindows: boolean): string { - let result: string[]; - let commandHasSpace = false; - let argHasSpace = false; - if (TaskDescription.hasUnescapedSpaces(command)) { - result.push(`"${command}"`); - commandHasSpace = true; - } else { - result.push(command); - } - if (args) { - for (let arg of args) { - if (TaskDescription.hasUnescapedSpaces(arg)) { - result.push(`"${arg}"`); - argHasSpace= true; - } else { - result.push(arg); - } - } - } - return result.join(' '); - } - -} -*/ diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index a8005416329..e2811234e63 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -62,6 +62,7 @@ export interface ITaskService { inTerminal(): boolean; isActive(): Promise; getActiveTasks(): Promise; + getBusyTasks(): Promise; restart(task: Task): void; terminate(task: Task): Promise; terminateAll(): Promise; diff --git a/src/vs/workbench/contrib/tasks/common/taskSystem.ts b/src/vs/workbench/contrib/tasks/common/taskSystem.ts index c844be5ec26..9c9654adbf6 100644 --- a/src/vs/workbench/contrib/tasks/common/taskSystem.ts +++ b/src/vs/workbench/contrib/tasks/common/taskSystem.ts @@ -133,6 +133,7 @@ export interface ITaskSystem { isActive(): Promise; isActiveSync(): boolean; getActiveTasks(): Task[]; + getBusyTasks(): Task[]; canAutoTerminate(): boolean; terminate(task: Task): Promise; terminateAll(): Promise; diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 0737e5fea02..66a00708221 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -274,7 +274,7 @@ export namespace PresentationOptions { export enum RuntimeType { Shell = 1, Process = 2, - CustomExecution2 = 3 + CustomExecution = 3 } export namespace RuntimeType { @@ -284,8 +284,8 @@ export namespace RuntimeType { return RuntimeType.Shell; case 'process': return RuntimeType.Process; - case 'customExecution2': - return RuntimeType.CustomExecution2; + case 'customExecution': + return RuntimeType.CustomExecution; default: return RuntimeType.Process; } @@ -380,7 +380,7 @@ export namespace TaskSourceKind { } export interface TaskSourceConfigElement { - workspaceFolder: IWorkspaceFolder; + workspaceFolder?: IWorkspaceFolder; workspace?: IWorkspace; file: string; index: number; @@ -680,8 +680,8 @@ export class CustomTask extends CommonTask { type = 'process'; break; - case RuntimeType.CustomExecution2: - type = 'customExecution2'; + case RuntimeType.CustomExecution: + type = 'customExecution'; break; case undefined: @@ -728,7 +728,7 @@ export class CustomTask extends CommonTask { return JSON.stringify(key); } - public getWorkspaceFolder(): IWorkspaceFolder { + public getWorkspaceFolder(): IWorkspaceFolder | undefined { return this._source.config.workspaceFolder; } diff --git a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts index 8110db9d80c..ba3adbd5a48 100644 --- a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts @@ -90,6 +90,10 @@ export class ProcessTaskSystem implements ITaskSystem { return result; } + public getBusyTasks(): Task[] { + return this.getActiveTasks(); + } + public run(task: Task): ITaskExecuteResult { if (this.activeTask) { return { kind: TaskExecuteKind.Active, task, active: { same: this.activeTask._id === task._id, background: this.activeTask.configurationProperties.isBackground! }, promise: this.activeTaskPromise! }; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index eae307ef62c..841a2b59bfe 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -251,6 +251,11 @@ configurationRegistry.registerConfiguration({ }, default: [] }, + 'terminal.integrated.allowChords': { + markdownDescription: nls.localize('terminal.integrated.allowChords', "Whether or not to allow chord keybindings in the terminal. Note that when this is true and the keystroke results in a chord it will bypass `terminal.integrated.commandsToSkipShell`, setting this to false is particularly useful when you want ctrl+k to go to your shell (not VS Code)."), + type: 'boolean', + default: true + }, 'terminal.integrated.inheritEnv': { markdownDescription: nls.localize('terminal.integrated.inheritEnv', "Whether new shells should inherit their environment from VS Code. This is not supported on Windows."), type: 'boolean', diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index f2600039f97..c3ede8986ad 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -10,7 +10,6 @@ import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShe import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, Platform } from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; -import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IDisposable } from 'vs/base/common/lifecycle'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { URI } from 'vs/base/common/uri'; @@ -65,7 +64,7 @@ export interface ITerminalTab { setVisible(visible: boolean): void; layout(width: number, height: number): void; addDisposable(disposable: IDisposable): void; - split(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance | undefined; + split(shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; } export interface ITerminalService { @@ -145,7 +144,7 @@ export interface ITerminalService { preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise; extHostReady(remoteAuthority: string): void; - requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; + requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): void; } @@ -227,7 +226,7 @@ export interface ITerminalInstance { * is the processes' exit code, an exit code of null means the process was killed as a result of * the ITerminalInstance being disposed. */ - onExit: Event; + onExit: Event; processReady: Promise; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index d3dde9111c4..ec49836256a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -232,8 +232,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public get commandTracker(): CommandTrackerAddon | undefined { return this._commandTrackerAddon; } public get navigationMode(): INavigationMode | undefined { return this._navigationModeAddon; } - private readonly _onExit = new Emitter(); - public get onExit(): Event { return this._onExit.event; } + private readonly _onExit = new Emitter(); + public get onExit(): Event { return this._onExit.event; } private readonly _onDisposed = new Emitter(); public get onDisposed(): Event { return this._onDisposed.event; } private readonly _onFocused = new Emitter(); @@ -258,7 +258,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public constructor( private readonly _terminalFocusContextKey: IContextKey, private readonly _configHelper: TerminalConfigHelper, - private _container: HTMLElement, + private _container: HTMLElement | undefined, private _shellLaunchConfig: IShellLaunchConfig, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -408,14 +408,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // The panel is minimized if (!this._isVisible) { return TerminalInstance._lastKnownCanvasDimensions; - } else { - // Trigger scroll event manually so that the viewport's scroll area is synced. This - // needs to happen otherwise its scrollTop value is invalid when the panel is toggled as - // it gets removed and then added back to the DOM (resetting scrollTop to 0). - // Upstream issue: https://github.com/sourcelair/xterm.js/issues/291 - if (this._xterm && this._xtermCore) { - this._xtermCore._onScroll.fire(this._xterm.buffer.viewportY); - } } if (!this._wrapperElement) { @@ -507,7 +499,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; }); } - this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, this._processManager, this._configHelper); + this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, xterm, this._processManager, this._configHelper); }); this._commandTrackerAddon = new CommandTrackerAddon(); @@ -548,7 +540,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } // The container changed, reattach - this._container.removeChild(this._wrapperElement); + if (this._container) { + this._container.removeChild(this._wrapperElement); + } this._container = container; this._container.appendChild(this._wrapperElement); } @@ -567,7 +561,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Attach the xterm object to the DOM, exposing it to the smoke tests this._wrapperElement.xterm = this._xterm; + this._wrapperElement.appendChild(this._xtermElement); + this._container.appendChild(this._wrapperElement); xterm.open(this._xtermElement); + xterm.textarea.addEventListener('focus', () => this._onFocus.fire(this)); xterm.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => { // Disable all input if the terminal is exiting @@ -579,7 +576,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // within commandsToSkipShell const standardKeyboardEvent = new StandardKeyboardEvent(event); const resolveResult = this._keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); - if (resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { + const allowChords = resolveResult && resolveResult.enterChord && this._configHelper.config.allowChords; + if (allowChords || resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { event.preventDefault(); return false; } @@ -644,9 +642,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._refreshSelectionContextKey(); })); - this._wrapperElement.appendChild(this._xtermElement); - this._container.appendChild(this._wrapperElement); - const widgetManager = new TerminalWidgetManager(this._wrapperElement); this._widgetManager = widgetManager; this._processManager.onProcessReady(() => { @@ -801,7 +796,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (this._wrapperElement.xterm) { this._wrapperElement.xterm = undefined; } - if (this._wrapperElement.parentElement) { + if (this._wrapperElement.parentElement && this._container) { this._container.removeChild(this._wrapperElement); } } @@ -900,6 +895,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // necessary if the number of rows in the terminal has decreased while it was in the // background since scrollTop changes take no effect but the terminal's position does // change since the number of visible rows decreases. + // This can likely be removed after https://github.com/xtermjs/xterm.js/issues/291 is + // fixed upstream. this._xtermCore._onScroll.fire(this._xterm.buffer.viewportY); if (this._container && this._container.parentElement) { // Force a layout when the instance becomes invisible. This is particularly important @@ -1105,7 +1102,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } - this._onExit.fire(exitCode || 0); + this._onExit.fire(exitCode); } private _attachPressAnyKeyToCloseListener(xterm: XTermTerminal) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 77ff7a72859..ea28486ab26 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -25,6 +25,7 @@ import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { assertIsDefined } from 'vs/base/common/types'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -70,7 +71,8 @@ export class TerminalPanel extends Panel { this._attachEventListeners(this._parentDomElement, this._terminalContainer); - this._terminalService.setContainers(this.getContainer(), this._terminalContainer); + const container = assertIsDefined(this.getContainer()); + this._terminalService.setContainers(container, this._terminalContainer); this._register(this.themeService.onThemeChange(theme => this._updateTheme(theme))); this._register(this._configurationService.onDidChangeConfiguration(e => { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index 345840a6410..3db8f795da5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -48,7 +48,7 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal constructor( public terminalId: number, shellLaunchConfig: IShellLaunchConfig, - activeWorkspaceRootUri: URI, + activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, configHelper: ITerminalConfigHelper, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 28d6d55692e..173957233b9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -11,13 +11,12 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; -import { IBrowserTerminalConfigHelper, ITerminalService, ITerminalInstance, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, ITerminalInstance, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; @@ -29,6 +28,7 @@ import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/termi import { isWindows, isMacintosh, OperatingSystem } from 'vs/base/common/platform'; import { basename } from 'vs/base/common/path'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; +import { find } from 'vs/base/common/arrays'; interface IExtHostReadyEntry { promise: Promise; @@ -54,7 +54,7 @@ export class TerminalService implements ITerminalService { public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; } - private _configHelper: IBrowserTerminalConfigHelper; + private _configHelper: TerminalConfigHelper; private _terminalContainer: HTMLElement | undefined; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } @@ -91,7 +91,6 @@ export class TerminalService implements ITerminalService { @IPanelService private _panelService: IPanelService, @IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService, @ILifecycleService lifecycleService: ILifecycleService, - @INotificationService private _notificationService: INotificationService, @IDialogService private _dialogService: IDialogService, @IInstantiationService private _instantiationService: IInstantiationService, @IExtensionService private _extensionService: IExtensionService, @@ -135,7 +134,7 @@ export class TerminalService implements ITerminalService { return activeInstance ? activeInstance : this.createTerminal(undefined); } - public requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void { + public requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void { this._extensionService.whenInstalledExtensionsRegistered().then(async () => { // Wait for the remoteAuthority to be ready (and listening for events) before firing // the event to spawn the ext host process @@ -392,12 +391,7 @@ export class TerminalService implements ITerminalService { return null; } - const instance = tab.split(this._terminalFocusContextKey, this.configHelper, shellLaunchConfig); - if (!instance) { - this._showNotEnoughSpaceToast(); - return null; - } - + const instance = tab.split(shellLaunchConfig); this._initInstanceListeners(instance); this._onInstancesChanged.fire(); @@ -414,13 +408,8 @@ export class TerminalService implements ITerminalService { instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); } - private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | null { - for (const tab of this._terminalTabs) { - if (tab.terminalInstances.indexOf(instance) !== -1) { - return tab; - } - } - return null; + private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined { + return find(this._terminalTabs, tab => tab.terminalInstances.indexOf(instance) !== -1); } public showPanel(focus?: boolean): Promise { @@ -499,10 +488,6 @@ export class TerminalService implements ITerminalService { return !res.confirmed; } - protected _showNotEnoughSpaceToast(): void { - this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); - } - protected _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { if (potentialPaths.length === 0) { return Promise.resolve(null); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 3d4d50cdc9f..68ff40e9135 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -14,7 +14,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITerminalInstance, Direction, ITerminalTab, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; const SPLIT_PANE_MIN_SIZE = 120; -const TERMINAL_MIN_USEFUL_SIZE = 250; class SplitPaneContainer extends Disposable { private _height: number; @@ -370,18 +369,11 @@ export class TerminalTab extends Disposable implements ITerminalTab { this.terminalInstances.forEach(i => i.setVisible(visible)); } - public split( - terminalFocusContextKey: IContextKey, - configHelper: ITerminalConfigHelper, - shellLaunchConfig: IShellLaunchConfig - ): ITerminalInstance | undefined { + public split(shellLaunchConfig: IShellLaunchConfig): ITerminalInstance { if (!this._container) { throw new Error('Cannot split terminal that has not been attached'); } - const newTerminalSize = ((this._panelPosition === Position.BOTTOM ? this._container.clientWidth : this._container.clientHeight) / (this._terminalInstances.length + 1)); - if (newTerminalSize < TERMINAL_MIN_USEFUL_SIZE) { - return undefined; - } + const instance = this._terminalService.createInstance(undefined, shellLaunchConfig); this._terminalInstances.splice(this._activeInstanceIndex + 1, 0, instance); this._initInstanceListeners(instance); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index fbc3cc37ea7..f587dbc700f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -101,6 +101,7 @@ export interface ITerminalConfiguration { detectLocale: 'auto' | 'off' | 'on'; scrollback: number; commandsToSkipShell: string[]; + allowChords: boolean; cwd: string; confirmOnExit: boolean; enableBell: boolean; @@ -341,7 +342,7 @@ export interface ITerminalProcessExtHostProxy extends IDisposable { export interface ISpawnExtHostProcessRequest { proxy: ITerminalProcessExtHostProxy; shellLaunchConfig: IShellLaunchConfig; - activeWorkspaceRootUri: URI; + activeWorkspaceRootUri: URI | undefined; cols: number; rows: number; isWorkspaceShellAllowed: boolean; diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts index fefece531e3..bdaf6d62248 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts @@ -84,7 +84,9 @@ suite('Workbench - TerminalCommandTracker', () => { (window).matchMedia = () => { return { addListener: () => { } }; }; - xterm.open(document.createElement('div')); + const e = document.createElement('div'); + document.body.appendChild(e); + xterm.open(e); await writePromise(xterm, '\r0'); await writePromise(xterm, '\n\r1'); @@ -109,12 +111,16 @@ suite('Workbench - TerminalCommandTracker', () => { assert.equal(xterm.getSelection(), '2'); commandTracker.selectToNextCommand(); assert.equal(xterm.getSelection(), isWindows ? '\r\n' : '\n'); + + document.body.removeChild(e); }); test('should select to the next and previous lines & commands', async () => { (window).matchMedia = () => { return { addListener: () => { } }; }; - xterm.open(document.createElement('div')); + const e = document.createElement('div'); + document.body.appendChild(e); + xterm.open(e); await writePromise(xterm, '\r0'); await writePromise(xterm, '\n\r1'); @@ -144,6 +150,8 @@ suite('Workbench - TerminalCommandTracker', () => { assert.equal(xterm.getSelection(), isWindows ? '1\r\n2' : '1\n2'); commandTracker.selectToPreviousLine(); assert.equal(xterm.getSelection(), isWindows ? '0\r\n1\r\n2' : '0\n1\n2'); + + document.body.removeChild(e); }); }); }); diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 5026e50adad..e389c436f63 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -14,7 +14,6 @@ import { IWorkbenchThemeService, COLOR_THEME_SETTING, ICON_THEME_SETTING, IColor import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { Delayer } from 'vs/base/common/async'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Color } from 'vs/base/common/color'; @@ -27,7 +26,7 @@ import { IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/commo export class SelectColorThemeAction extends Action { static readonly ID = 'workbench.action.selectTheme'; - static LABEL = localize('selectTheme.label', "Color Theme"); + static readonly LABEL = localize('selectTheme.label', "Color Theme"); constructor( id: string, @@ -52,34 +51,43 @@ export class SelectColorThemeAction extends Action { ...configurationEntries(this.extensionGalleryService, localize('installColorThemes', "Install Additional Color Themes...")) ]; - const selectTheme = (theme: ThemeItem, applyTheme: boolean) => { - let themeId = theme.id; - if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry - if (applyTheme) { - openExtensionViewlet(this.viewletService, 'category:themes '); - } - themeId = currentTheme.id; - } - let target: ConfigurationTarget | undefined = undefined; - if (applyTheme) { - let confValue = this.configurationService.inspect(COLOR_THEME_SETTING); - target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; - } + let selectThemeTimeout: number | undefined; - this.themeService.setColorTheme(themeId, target).then(undefined, - err => { - onUnexpectedError(err); - this.themeService.setColorTheme(currentTheme.id, undefined); + const selectTheme = (theme: ThemeItem, applyTheme: boolean) => { + if (selectThemeTimeout) { + clearTimeout(selectThemeTimeout); + } + selectThemeTimeout = window.setTimeout(() => { + selectThemeTimeout = undefined; + + let themeId = theme.id; + if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry + if (applyTheme) { + openExtensionViewlet(this.viewletService, 'category:themes '); + } + themeId = currentTheme.id; } - ); + let target: ConfigurationTarget | undefined = undefined; + if (applyTheme) { + let confValue = this.configurationService.inspect(COLOR_THEME_SETTING); + target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; + } + + this.themeService.setColorTheme(themeId, target).then(undefined, + err => { + onUnexpectedError(err); + this.themeService.setColorTheme(currentTheme.id, undefined); + } + ); + }, applyTheme ? 0 : 200); }; const placeHolder = localize('themes.selectTheme', "Select Color Theme (Up/Down Keys to Preview)"); const autoFocusIndex = firstIndex(picks, p => isItem(p) && p.id === currentTheme.id); const activeItem: ThemeItem = picks[autoFocusIndex] as ThemeItem; - const delayer = new Delayer(100); - const chooseTheme = (theme: ThemeItem) => delayer.trigger(() => selectTheme(theme || currentTheme, true), 0); - const tryTheme = (theme: ThemeItem) => delayer.trigger(() => selectTheme(theme, false)); + + const chooseTheme = (theme: ThemeItem) => selectTheme(theme || currentTheme, true); + const tryTheme = (theme: ThemeItem) => selectTheme(theme, false); return this.quickInputService.pick(picks, { placeHolder, activeItem, onDidFocus: tryTheme }) .then(chooseTheme); @@ -90,7 +98,7 @@ export class SelectColorThemeAction extends Action { class SelectIconThemeAction extends Action { static readonly ID = 'workbench.action.selectIconTheme'; - static LABEL = localize('selectIconTheme.label', "File Icon Theme"); + static readonly LABEL = localize('selectIconTheme.label', "File Icon Theme"); constructor( id: string, @@ -115,33 +123,40 @@ class SelectIconThemeAction extends Action { configurationEntries(this.extensionGalleryService, localize('installIconThemes', "Install Additional File Icon Themes...")) ); + let selectThemeTimeout: number | undefined; + const selectTheme = (theme: ThemeItem, applyTheme: boolean) => { - let themeId = theme.id; - if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry + if (selectThemeTimeout) { + clearTimeout(selectThemeTimeout); + } + selectThemeTimeout = window.setTimeout(() => { + selectThemeTimeout = undefined; + let themeId = theme.id; + if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry + if (applyTheme) { + openExtensionViewlet(this.viewletService, 'tag:icon-theme '); + } + themeId = currentTheme.id; + } + let target: ConfigurationTarget | undefined = undefined; if (applyTheme) { - openExtensionViewlet(this.viewletService, 'tag:icon-theme '); + let confValue = this.configurationService.inspect(ICON_THEME_SETTING); + target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; } - themeId = currentTheme.id; - } - let target: ConfigurationTarget | undefined = undefined; - if (applyTheme) { - let confValue = this.configurationService.inspect(ICON_THEME_SETTING); - target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; - } - this.themeService.setFileIconTheme(themeId, target).then(undefined, - err => { - onUnexpectedError(err); - this.themeService.setFileIconTheme(currentTheme.id, undefined); - } - ); + this.themeService.setFileIconTheme(themeId, target).then(undefined, + err => { + onUnexpectedError(err); + this.themeService.setFileIconTheme(currentTheme.id, undefined); + } + ); + }, applyTheme ? 0 : 200); }; const placeHolder = localize('themes.selectIconTheme', "Select File Icon Theme"); const autoFocusIndex = firstIndex(picks, p => isItem(p) && p.id === currentTheme.id); const activeItem: ThemeItem = picks[autoFocusIndex] as ThemeItem; - const delayer = new Delayer(100); - const chooseTheme = (theme: ThemeItem) => delayer.trigger(() => selectTheme(theme || currentTheme, true), 0); - const tryTheme = (theme: ThemeItem) => delayer.trigger(() => selectTheme(theme, false)); + const chooseTheme = (theme: ThemeItem) => selectTheme(theme || currentTheme, true); + const tryTheme = (theme: ThemeItem) => selectTheme(theme, false); return this.quickInputService.pick(picks, { placeHolder, activeItem, onDidFocus: tryTheme }) .then(chooseTheme); @@ -197,7 +212,7 @@ function toEntries(themes: Array, label?: string): class GenerateColorThemeAction extends Action { static readonly ID = 'workbench.action.generateColorTheme'; - static LABEL = localize('generateColorTheme.label', "Generate Color Theme From Current Settings"); + static readonly LABEL = localize('generateColorTheme.label', "Generate Color Theme From Current Settings"); constructor( id: string, @@ -288,4 +303,4 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { title: localize('themes.selectIconTheme.label', "File Icon Theme") }, order: 2 -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index 632e7c74330..21e80346285 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -17,7 +17,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IRequestService, asText } from 'vs/platform/request/common/request'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; @@ -43,7 +43,7 @@ export class ReleaseNotesManager { @ITelemetryService private readonly _telemetryService: ITelemetryService, @IEditorService private readonly _editorService: IEditorService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, - @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService, + @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @IExtensionService private readonly _extensionService: IExtensionService, @IProductService private readonly _productService: IProductService ) { @@ -71,9 +71,9 @@ export class ReleaseNotesManager { if (this._currentReleaseNotes) { this._currentReleaseNotes.setName(title); this._currentReleaseNotes.webview.html = html; - this._webviewEditorService.revealWebview(this._currentReleaseNotes, activeControl ? activeControl.group : this._editorGroupService.activeGroup, false); + this._webviewWorkbenchService.revealWebview(this._currentReleaseNotes, activeControl ? activeControl.group : this._editorGroupService.activeGroup, false); } else { - this._currentReleaseNotes = this._webviewEditorService.createWebview( + this._currentReleaseNotes = this._webviewWorkbenchService.createWebview( generateUuid(), 'releaseNotes', title, diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index c93c32fede6..ed9938dc7dc 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -103,7 +103,7 @@ export class ShowReleaseNotesAction extends AbstractShowReleaseNotesAction { export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction { static readonly ID = ShowCurrentReleaseNotesActionId; - static LABEL = nls.localize('showReleaseNotes', "Show Release Notes"); + static readonly LABEL = nls.localize('showReleaseNotes', "Show Release Notes"); constructor( id = ShowCurrentReleaseNotesAction.ID, @@ -469,7 +469,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu group: '6_update', command: { id: 'update.restart', - title: nls.localize('restartToUpdate', "Restart to Update") + title: nls.localize('restartToUpdate', "*Restart to Update") }, when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Ready) }); diff --git a/src/vs/workbench/contrib/update/electron-browser/update.contribution.ts b/src/vs/workbench/contrib/update/electron-browser/update.contribution.ts deleted file mode 100644 index 26a9151d09c..00000000000 --- a/src/vs/workbench/contrib/update/electron-browser/update.contribution.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as platform from 'vs/base/common/platform'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { Win3264BitContribution } from 'vs/workbench/contrib/update/electron-browser/update'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; - -const workbench = Registry.as(WorkbenchExtensions.Workbench); - -if (platform.isWindows) { - if (process.arch === 'ia32') { - workbench.registerWorkbenchContribution(Win3264BitContribution, LifecyclePhase.Restored); - } -} diff --git a/src/vs/workbench/contrib/update/electron-browser/update.ts b/src/vs/workbench/contrib/update/electron-browser/update.ts deleted file mode 100644 index b8fbf48c2de..00000000000 --- a/src/vs/workbench/contrib/update/electron-browser/update.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import severity from 'vs/base/common/severity'; -import product from 'vs/platform/product/common/product'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { INotificationService } from 'vs/platform/notification/common/notification'; - -export class Win3264BitContribution implements IWorkbenchContribution { - - private static readonly URL = 'https://code.visualstudio.com/updates/v1_15#_windows-64-bit'; - private static readonly INSIDER_URL = 'https://github.com/Microsoft/vscode-docs/blob/vnext/release-notes/v1_15.md#windows-64-bit'; - - constructor( - @INotificationService notificationService: INotificationService, - @IEnvironmentService environmentService: IEnvironmentService - ) { - if (environmentService.disableUpdates) { - return; - } - - const url = product.quality === 'insider' - ? Win3264BitContribution.INSIDER_URL - : Win3264BitContribution.URL; - - notificationService.prompt( - severity.Info, - nls.localize('64bitisavailable', "{0} for 64-bit Windows is now available! Click [here]({1}) to learn more.", product.nameShort, url), - [], - { - sticky: true, - neverShowAgain: { id: 'neverShowAgain:update/win32-64bits', isSecondary: true } - } - ); - } -} diff --git a/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts b/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts index ba66812aab7..d007281fc3b 100644 --- a/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts +++ b/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts @@ -33,8 +33,7 @@ const TRUSTED_DOMAINS_STAT: IStat = { size: 0 }; -const CONFIG_HELP_TEXT_PRE = - `// Links matching one or more entries in the list below can be opened without link protection. +const CONFIG_HELP_TEXT_PRE = `// Links matching one or more entries in the list below can be opened without link protection. // The following examples show what entries can look like: // - "https://microsoft.com": Matches this specific domain using https // - "https://*.microsoft.com": Match all domains ending in "microsoft.com" using https @@ -95,23 +94,36 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProvider, IW } readFile(resource: URI): Promise { - const { defaultTrustedDomains, trustedDomains } = readTrustedDomains(this.storageService, this.productService); + let trustedDomainsContent = this.storageService.get( + 'http.linkProtectionTrustedDomainsContent', + StorageScope.GLOBAL + ); + + if ( + !trustedDomainsContent || + trustedDomainsContent.indexOf(CONFIG_HELP_TEXT_PRE) === -1 || + trustedDomainsContent.indexOf(CONFIG_HELP_TEXT_AFTER) === -1 + ) { + const { defaultTrustedDomains, trustedDomains } = readTrustedDomains(this.storageService, this.productService); + + trustedDomainsContent = computeTrustedDomainContent(defaultTrustedDomains, trustedDomains); + } - const trustedDomainsContent = computeTrustedDomainContent(defaultTrustedDomains, trustedDomains); const buffer = VSBuffer.fromString(trustedDomainsContent).buffer; return Promise.resolve(buffer); } writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { try { - const trustedDomains = parse(content.toString()); + const trustedDomainsContent = content.toString(); + const trustedDomains = parse(trustedDomainsContent); + this.storageService.store('http.linkProtectionTrustedDomainsContent', trustedDomainsContent, StorageScope.GLOBAL); this.storageService.store( 'http.linkProtectionTrustedDomains', JSON.stringify(trustedDomains), StorageScope.GLOBAL ); - } catch (err) { } return Promise.resolve(); diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 4ba68ecd95b..10c3edf2150 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -35,7 +35,7 @@ export class OpenUrlAction extends Action { run(): Promise { return this.quickInputService.input({ prompt: 'URL to open' }).then(input => { const uri = URI.parse(input); - this.urlService.open(uri); + this.urlService.open(uri, { trusted: true }); }); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index ffbbcf5ebd5..92cb1c09c88 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -116,7 +116,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } else if (this.userDataSyncService.status === SyncStatus.HasConflicts) { badge = new NumberBadge(1, () => localize('resolve conflicts', "Resolve Conflicts")); } else if (this.userDataSyncService.status === SyncStatus.Syncing) { - badge = new ProgressBadge(() => localize('syncing', "Synchronising User Configuration...")); + badge = new ProgressBadge(() => localize('syncing', "Synchronizing User Configuration...")); clazz = 'progress-badge'; } diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index e9a4d74f213..e68c960a1f4 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -28,6 +28,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IDimension } from 'vs/platform/layout/browser/layoutService'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { assertIsDefined } from 'vs/base/common/types'; const $ = dom.$; @@ -122,7 +123,7 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr } private create(): void { - const container = this.layoutService.getContainer(Parts.EDITOR_PART); + const container = assertIsDefined(this.layoutService.getContainer(Parts.EDITOR_PART)); container.classList.add('has-watermark'); this.watermark = $('.watermark'); @@ -168,7 +169,9 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr this.watermark.remove(); const container = this.layoutService.getContainer(Parts.EDITOR_PART); - container.classList.remove('has-watermark'); + if (container) { + container.classList.remove('has-watermark'); + } this.watermarkDisposable.clear(); } diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index e052f0cccaa..31f778e3bfc 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -11,6 +11,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { Dimension } from 'vs/base/browser/dom'; +import { assertIsDefined } from 'vs/base/common/types'; /** * Webview editor overlay that creates and destroys the underlying webview as needed. @@ -47,7 +48,11 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd public get container() { const container = document.createElement('div'); container.id = `webview-${this.id}`; - this._layoutService.getContainer(Parts.EDITOR_PART).appendChild(container); + container.style.visibility = 'hidden'; + + const editorPart = assertIsDefined(this._layoutService.getContainer(Parts.EDITOR_PART)); + editorPart.appendChild(container); + return container; } @@ -171,14 +176,6 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd } } - update(html: string, options: WebviewContentOptions, retainContextWhenHidden: boolean): void { - this._contentOptions = options; - this._html = html; - this.withWebview(webview => { - webview.update(html, options, retainContextWhenHidden); - }); - } - layout(): void { this.withWebview(webview => webview.layout()); } focus(): void { this.withWebview(webview => webview.focus()); } reload(): void { this.withWebview(webview => webview.reload()); } diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 1c5572ec400..2189e68d81f 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -19,7 +19,7 @@ import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategor import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands'; import { WebviewEditor } from '../browser/webviewEditor'; import { WebviewInput } from '../browser/webviewEditorInput'; -import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService'; +import { IWebviewWorkbenchService, WebviewEditorService } from './webviewWorkbenchService'; (Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( WebviewEditor, @@ -31,7 +31,7 @@ Registry.as(EditorInputExtensions.EditorInputFactor WebviewEditorInputFactory.ID, WebviewEditorInputFactory); -registerSingleton(IWebviewEditorService, WebviewEditorService, true); +registerSingleton(IWebviewWorkbenchService, WebviewEditorService, true); const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 271ed303794..083550bf21d 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -73,11 +73,6 @@ export interface Webview extends IDisposable { readonly onMissingCsp: Event; sendMessage(data: any): void; - update( - html: string, - options: WebviewContentOptions, - retainContextWhenHidden: boolean - ): void; layout(): void; focus(): void; diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index 9f341bbb06c..cb620c83d9d 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -21,7 +21,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic export class WebviewEditor extends BaseEditor { - public static ID = 'WebviewEditor'; + public static readonly ID = 'WebviewEditor'; private readonly _scopedContextKeyService = this._register(new MutableDisposable()); private _findWidgetVisible: IContextKey; diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index ee87e913ab7..ef25742c11f 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -2,13 +2,15 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as dom from 'vs/base/browser/dom'; import { memoize } from 'vs/base/common/decorators'; +import { Lazy } from 'vs/base/common/lazy'; +import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { EditorInput, EditorModel, GroupIdentifier, IEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; -import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle'; const WebviewPanelResourceScheme = 'webview-panel'; @@ -60,19 +62,19 @@ export class WebviewInput extends EditorInput { private _name: string; private _iconPath?: { light: URI, dark: URI }; private _group?: GroupIdentifier; - private readonly _webview: WebviewEditorOverlay; + private readonly _webview: Lazy; constructor( public readonly id: string, public readonly viewType: string, name: string, - webview: Unowned + webview: Lazy> ) { super(); this._name = name; - this._webview = this._register(webview.acquire()); // The input owns this webview + this._webview = webview.map(value => this._register(value.acquire())); // The input owns this webview } public getTypeId(): string { @@ -94,7 +96,7 @@ export class WebviewInput extends EditorInput { return this.getName(); } - public getDescription() { + public getDescription(): string | undefined { return undefined; } @@ -103,12 +105,12 @@ export class WebviewInput extends EditorInput { this._onDidChangeLabel.fire(); } - public get webview() { - return this._webview; + public get webview(): WebviewEditorOverlay { + return this._webview.getValue(); } public get extension() { - return this._webview.extension; + return this._webview.getValue().extension; } public get iconPath() { @@ -140,25 +142,3 @@ export class WebviewInput extends EditorInput { this._group = group; } } - -export class RevivedWebviewEditorInput extends WebviewInput { - private _revived: boolean = false; - - constructor( - id: string, - viewType: string, - name: string, - private readonly reviver: (input: WebviewInput) => Promise, - webview: Unowned - ) { - super(id, viewType, name, webview); - } - - public async resolve(): Promise { - if (!this._revived) { - this._revived = true; - await this.reviver(this); - } - return super.resolve(); - } -} diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts index 288c2c9e537..50150f357de 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts @@ -9,7 +9,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorInputFactory } from 'vs/workbench/common/editor'; import { WebviewInput } from './webviewEditorInput'; -import { IWebviewEditorService, WebviewInputOptions } from './webviewEditorService'; +import { IWebviewWorkbenchService, WebviewInputOptions } from './webviewWorkbenchService'; interface SerializedIconPath { light: string | UriComponents; @@ -33,11 +33,11 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { public static readonly ID = WebviewInput.typeId; public constructor( - @IWebviewEditorService private readonly _webviewService: IWebviewEditorService + @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService ) { } public serialize(input: WebviewInput): string | undefined { - if (!this._webviewService.shouldPersist(input)) { + if (!this._webviewWorkbenchService.shouldPersist(input)) { return undefined; } @@ -54,7 +54,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { serializedEditorInput: string ): WebviewInput { const data = this.fromJson(serializedEditorInput); - return this._webviewService.reviveWebview(data.id || generateUuid(), data.viewType, data.title, data.iconPath, data.state, data.options, data.extensionLocation ? { + return this._webviewWorkbenchService.reviveWebview(data.id || generateUuid(), data.viewType, data.title, data.iconPath, data.state, data.options, data.extensionLocation && data.extensionId ? { location: data.extensionLocation, id: data.extensionId } : undefined, data.group); diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 6b0c7543233..a80e6b551c0 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -12,12 +12,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; -import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; import { loadLocalResource } from 'vs/workbench/contrib/webview/common/resourceLoader'; -import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; interface WebviewContent { @@ -45,7 +44,7 @@ export class IFrameWebview extends Disposable implements Webview { private readonly id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, - @IThemeService themeService: IThemeService, + private readonly webviewThemeDataProvider: WebviewThemeDataProvider, @ITunnelService tunnelService: ITunnelService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IFileService private readonly fileService: IFileService, @@ -146,8 +145,8 @@ export class IFrameWebview extends Disposable implements Webview { })); }); - this.style(themeService.getTheme()); - this._register(themeService.onThemeChange(this.style, this)); + this.style(); + this._register(webviewThemeDataProvider.onThemeDataChanged(this.style, this)); } private get externalEndpoint(): string { @@ -195,18 +194,6 @@ export class IFrameWebview extends Disposable implements Webview { `${startQuote}${this.externalEndpoint}/vscode-resource/file${path}${endQuote}`); } - public update(html: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { - if (retainContextWhenHidden && html === this.content.html && areWebviewInputOptionsEqual(options, this.content.options)) { - return; - } - this.content = { - html: this.preprocessHtml(html), - options: options, - state: this.content.state, - }; - this.doUpdateContent(); - } - private doUpdateContent() { this._send('content', { contents: this.content.html, @@ -307,8 +294,8 @@ export class IFrameWebview extends Disposable implements Webview { .catch(err => console.error(err)); } - private style(theme: ITheme): void { - const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService); + private style(): void { + const { styles, activeTheme } = this.webviewThemeDataProvider.getWebviewThemeData(); this._send('styles', { styles, activeTheme }); } diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts index 21333aca8bc..44610b046b8 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewService.ts @@ -3,25 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; -import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { DynamicWebviewEditorOverlay } from './dynamicWebviewEditorOverlay'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; +import { DynamicWebviewEditorOverlay } from './dynamicWebviewEditorOverlay'; export class WebviewService implements IWebviewService { _serviceBrand: undefined; + private readonly _webviewThemeDataProvider: WebviewThemeDataProvider; + constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, - ) { } + ) { + this._webviewThemeDataProvider = this._instantiationService.createInstance(WebviewThemeDataProvider); + } createWebview( id: string, options: WebviewOptions, contentOptions: WebviewContentOptions ): WebviewElement { - return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions); + return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, this._webviewThemeDataProvider); } createWebviewEditorOverlay( diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts similarity index 72% rename from src/vs/workbench/contrib/webview/browser/webviewEditorService.ts rename to src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts index 5e48e6bc5da..c0e1994c206 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts @@ -4,85 +4,30 @@ *--------------------------------------------------------------------------------------------*/ import { equals } from 'vs/base/common/arrays'; +import { memoize } from 'vs/base/common/decorators'; import { IDisposable, toDisposable, UnownedDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; +import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { EditorActivation, IEditorModel } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { GroupIdentifier } from 'vs/workbench/common/editor'; -import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; -import { RevivedWebviewEditorInput, WebviewInput } from './webviewEditorInput'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { EditorActivation } from 'vs/platform/editor/common/editor'; +import { WebviewInput } from './webviewEditorInput'; +import { Lazy } from 'vs/base/common/lazy'; -export const IWebviewEditorService = createDecorator('webviewEditorService'); +export const IWebviewWorkbenchService = createDecorator('webviewEditorService'); export interface ICreateWebViewShowOptions { group: IEditorGroup | GroupIdentifier | ACTIVE_GROUP_TYPE | SIDE_GROUP_TYPE; preserveFocus: boolean; } -export interface IWebviewEditorService { - _serviceBrand: undefined; - - createWebview( - id: string, - viewType: string, - title: string, - showOptions: ICreateWebViewShowOptions, - options: WebviewInputOptions, - extension: undefined | { - location: URI, - id: ExtensionIdentifier - }, - ): WebviewInput; - - reviveWebview( - id: string, - viewType: string, - title: string, - iconPath: { light: URI, dark: URI } | undefined, - state: any, - options: WebviewInputOptions, - extension: undefined | { - readonly location: URI, - readonly id?: ExtensionIdentifier - }, - group: number | undefined - ): WebviewInput; - - revealWebview( - webview: WebviewInput, - group: IEditorGroup, - preserveFocus: boolean - ): void; - - registerResolver( - reviver: WebviewResolve - ): IDisposable; - - shouldPersist( - input: WebviewInput - ): boolean; - - resolveWebview( - webview: WebviewInput, - ): Promise; -} - -export interface WebviewResolve { - canResolve( - webview: WebviewInput, - ): boolean; - - resolveWebview( - webview: WebviewInput, - ): Promise; -} - export interface WebviewInputOptions extends WebviewOptions, WebviewContentOptions { readonly tryRestoreScrollPosition?: boolean; readonly retainContextWhenHidden?: boolean; @@ -95,17 +40,94 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn && a.allowScripts === b.allowScripts && a.retainContextWhenHidden === b.retainContextWhenHidden && a.tryRestoreScrollPosition === b.tryRestoreScrollPosition - && (a.localResourceRoots === b.localResourceRoots || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString()))) - && (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort))); + && equals(a.localResourceRoots, b.localResourceRoots, isEqual) + && equals(a.portMapping, b.portMapping, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort); } -function canRevive(reviver: WebviewResolve, webview: WebviewInput): boolean { +export interface WebviewExtensionDescription { + readonly location: URI; + readonly id: ExtensionIdentifier; +} + +export interface IWebviewWorkbenchService { + _serviceBrand: undefined; + + createWebview( + id: string, + viewType: string, + title: string, + showOptions: ICreateWebViewShowOptions, + options: WebviewInputOptions, + extension: WebviewExtensionDescription | undefined, + ): WebviewInput; + + reviveWebview( + id: string, + viewType: string, + title: string, + iconPath: { light: URI, dark: URI } | undefined, + state: any, + options: WebviewInputOptions, + extension: WebviewExtensionDescription | undefined, + group: number | undefined + ): WebviewInput; + + revealWebview( + webview: WebviewInput, + group: IEditorGroup, + preserveFocus: boolean + ): void; + + registerResolver( + resolver: WebviewResolver + ): IDisposable; + + shouldPersist( + input: WebviewInput + ): boolean; + + resolveWebview( + webview: WebviewInput, + ): Promise; +} + +export interface WebviewResolver { + canResolve( + webview: WebviewInput, + ): boolean; + + resolveWebview( + webview: WebviewInput, + ): Promise; +} + +function canRevive(reviver: WebviewResolver, webview: WebviewInput): boolean { if (webview.isDisposed()) { return false; } return reviver.canResolve(webview); } + +export class LazilyResolvedWebviewEditorInput extends WebviewInput { + constructor( + id: string, + viewType: string, + name: string, + webview: Lazy>, + @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, + ) { + super(id, viewType, name, webview); + } + + @memoize + public async resolve(): Promise { + await this._webviewWorkbenchService.resolveWebview(this); + return super.resolve(); + } +} + + class RevivalPool { private _awaitingRevival: Array<{ input: WebviewInput, resolve: () => void }> = []; @@ -113,7 +135,7 @@ class RevivalPool { this._awaitingRevival.push({ input, resolve }); } - public reviveFor(reviver: WebviewResolve) { + public reviveFor(reviver: WebviewResolver) { const toRevive = this._awaitingRevival.filter(({ input }) => canRevive(reviver, input)); this._awaitingRevival = this._awaitingRevival.filter(({ input }) => !canRevive(reviver, input)); @@ -123,15 +145,14 @@ class RevivalPool { } } -export class WebviewEditorService implements IWebviewEditorService { +export class WebviewEditorService implements IWebviewWorkbenchService { _serviceBrand: undefined; - private readonly _revivers = new Set(); + private readonly _revivers = new Set(); private readonly _revivalPool = new RevivalPool(); constructor( @IEditorService private readonly _editorService: IEditorService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IWebviewService private readonly _webviewService: IWebviewService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @@ -143,14 +164,10 @@ export class WebviewEditorService implements IWebviewEditorService { title: string, showOptions: ICreateWebViewShowOptions, options: WebviewInputOptions, - extension: undefined | { - location: URI, - id: ExtensionIdentifier - }, + extension: WebviewExtensionDescription | undefined, ): WebviewInput { - const webview = this.createWebiew(id, extension, options); - - const webviewInput = this._instantiationService.createInstance(WebviewInput, id, viewType, title, new UnownedDisposable(webview), undefined); + const webview = new Lazy(() => new UnownedDisposable(this.createWebiew(id, extension, options))); + const webviewInput = new WebviewInput(id, viewType, title, webview); this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus, @@ -188,28 +205,16 @@ export class WebviewEditorService implements IWebviewEditorService { iconPath: { light: URI, dark: URI } | undefined, state: any, options: WebviewInputOptions, - extension: undefined | { - readonly location: URI, - readonly id: ExtensionIdentifier - }, + extension: WebviewExtensionDescription | undefined, group: number | undefined, ): WebviewInput { - const webview = this.createWebiew(id, extension, options); - webview.state = state; - - const webviewInput = new RevivedWebviewEditorInput(id, viewType, title, async (webview: WebviewInput): Promise => { - const didRevive = await this.tryRevive(webview); - if (didRevive) { - return Promise.resolve(undefined); - } - - // A reviver may not be registered yet. Put into pool and resolve promise when we can revive - let resolve: () => void; - const promise = new Promise(r => { resolve = r; }); - this._revivalPool.add(webview, resolve!); - return promise; - }, new UnownedDisposable(webview)); + const webview = new Lazy(() => { + const webview = this.createWebiew(id, extension, options); + webview.state = state; + return new UnownedDisposable(webview); + }); + const webviewInput = new LazilyResolvedWebviewEditorInput(id, viewType, title, webview, this); webviewInput.iconPath = iconPath; if (typeof group === 'number') { @@ -219,7 +224,7 @@ export class WebviewEditorService implements IWebviewEditorService { } public registerResolver( - reviver: WebviewResolve + reviver: WebviewResolver ): IDisposable { this._revivers.add(reviver); this._revivalPool.reviveFor(reviver); @@ -238,7 +243,7 @@ export class WebviewEditorService implements IWebviewEditorService { // Revived webviews may not have an actively registered reviver but we still want to presist them // since a reviver should exist when it is actually needed. - return webview instanceof RevivedWebviewEditorInput; + return webview instanceof LazilyResolvedWebviewEditorInput; } private async tryRevive( @@ -258,11 +263,15 @@ export class WebviewEditorService implements IWebviewEditorService { ): Promise { const didRevive = await this.tryRevive(webview); if (!didRevive) { - this._revivalPool.add(webview, () => { }); + // A reviver may not be registered yet. Put into pool and resolve promise when we can revive + let resolve: () => void; + const promise = new Promise(r => { resolve = r; }); + this._revivalPool.add(webview, resolve!); + return promise; } } - private createWebiew(id: string, extension: { location: URI; id: ExtensionIdentifier; } | undefined, options: WebviewInputOptions) { + private createWebiew(id: string, extension: WebviewExtensionDescription | undefined, options: WebviewInputOptions) { const webview = this._webviewService.createWebviewEditorOverlay(id, { enableFindWidget: options.enableFindWidget, retainContextWhenHidden: options.retainContextWhenHidden @@ -286,4 +295,4 @@ export class WebviewEditorService implements IWebviewEditorService { } } -registerSingleton(IWebviewEditorService, WebviewEditorService, true); +registerSingleton(IWebviewWorkbenchService, WebviewEditorService, true); diff --git a/src/vs/workbench/contrib/webview/common/themeing.ts b/src/vs/workbench/contrib/webview/common/themeing.ts index 7c39b7a4bdd..9b2684fd3ae 100644 --- a/src/vs/workbench/contrib/webview/common/themeing.ts +++ b/src/vs/workbench/contrib/webview/common/themeing.ts @@ -3,45 +3,83 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { createMemoizer } from 'vs/base/common/decorators'; +import { Disposable } from 'vs/base/common/lifecycle'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService'; +import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; +import { Emitter } from 'vs/base/common/event'; interface WebviewThemeData { readonly activeTheme: string; - readonly styles: { readonly [key: string]: string | number }; + readonly styles: { readonly [key: string]: string | number; }; } -export function getWebviewThemeData( - theme: ITheme, - configurationService: IConfigurationService -): WebviewThemeData { - const configuration = configurationService.getValue('editor'); - const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; - const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight; - const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize; +export class WebviewThemeDataProvider extends Disposable { - const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => { - const color = theme.getColor(entry.id); - if (color) { - colors['vscode-' + entry.id.replace('.', '-')] = color.toString(); - } - return colors; - }, {} as { [key: string]: string }); - const styles = { - 'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif', - 'vscode-font-weight': 'normal', - 'vscode-font-size': '13px', - 'vscode-editor-font-family': editorFontFamily, - 'vscode-editor-font-weight': editorFontWeight, - 'vscode-editor-font-size': editorFontSize, - ...exportedColors - }; + private static readonly MEMOIZER = createMemoizer(); - const activeTheme = ApiThemeClassName.fromTheme(theme); - return { styles, activeTheme }; + private readonly _onThemeDataChanged = this._register(new Emitter()); + public readonly onThemeDataChanged = this._onThemeDataChanged.event; + + constructor( + @IThemeService private readonly _themeService: IThemeService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + ) { + super(); + + this._register(this._themeService.onThemeChange(() => { + this.reset(); + })); + + const webviewConfigurationKeys = ['editor.fontFamily', 'editor.fontWeight', 'editor.fontSize']; + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (webviewConfigurationKeys.some(key => e.affectsConfiguration(key))) { + this.reset(); + } + })); + } + + public getTheme(): ITheme { + return this._themeService.getTheme(); + } + + @WebviewThemeDataProvider.MEMOIZER + public getWebviewThemeData(): WebviewThemeData { + const configuration = this._configurationService.getValue('editor'); + const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; + const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight; + const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize; + + const theme = this._themeService.getTheme(); + const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => { + const color = theme.getColor(entry.id); + if (color) { + colors['vscode-' + entry.id.replace('.', '-')] = color.toString(); + } + return colors; + }, {} as { [key: string]: string; }); + + const styles = { + 'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif', + 'vscode-font-weight': 'normal', + 'vscode-font-size': '13px', + 'vscode-editor-font-family': editorFontFamily, + 'vscode-editor-font-weight': editorFontWeight, + 'vscode-editor-font-size': editorFontSize, + ...exportedColors + }; + + const activeTheme = ApiThemeClassName.fromTheme(theme); + return { styles, activeTheme }; + } + + private reset() { + WebviewThemeDataProvider.MEMOIZER.clear(); + this._onThemeDataChanged.fire(); + } } enum ApiThemeClassName { @@ -52,12 +90,10 @@ enum ApiThemeClassName { namespace ApiThemeClassName { export function fromTheme(theme: ITheme): ApiThemeClassName { - if (theme.type === LIGHT) { - return ApiThemeClassName.light; - } else if (theme.type === DARK) { - return ApiThemeClassName.dark; - } else { - return ApiThemeClassName.highContrast; + switch (theme.type) { + case LIGHT: return ApiThemeClassName.light; + case DARK: return ApiThemeClassName.dark; + default: return ApiThemeClassName.highContrast; } } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js index 6992535ea97..8be7c9520a6 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js +++ b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js @@ -13,15 +13,6 @@ return; } hasRegistered = true; - - // @ts-ignore - require('electron').webFrame.registerURLSchemeAsPrivileged('vscode-resource', { - secure: true, - bypassCSP: false, - allowServiceWorkers: false, - supportFetchAPI: true, - corsEnabled: true - }); }; }()); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index c13df0b47da..9cd4185fd76 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -11,20 +11,19 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; import { WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/resourceLoader'; -import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; -import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService'; +import { areWebviewInputOptionsEqual } from '../browser/webviewWorkbenchService'; import { WebviewFindDelegate, WebviewFindWidget } from '../browser/webviewFindWidget'; interface IKeydownEvent { @@ -38,8 +37,45 @@ interface IKeydownEvent { repeat: boolean; } +class WebviewTagHandle extends Disposable { + + private _webContents: undefined | WebContents | 'destroyed'; + + public constructor( + public readonly webview: WebviewTag, + ) { + super(); + + this._register(addDisposableListener(this.webview, 'destroyed', () => { + this._webContents = 'destroyed'; + })); + + this._register(addDisposableListener(this.webview, 'did-start-loading', once(() => { + const contents = this.webContents; + if (contents) { + this._onFirstLoad.fire(contents); + } + }))); + } + + private readonly _onFirstLoad = this._register(new Emitter()); + public readonly onFirstLoad = this._onFirstLoad.event; + + public get webContents(): WebContents | undefined { + if (this._webContents === 'destroyed') { + return undefined; + } + if (this._webContents) { + return this._webContents; + } + this._webContents = this.webview.getWebContents(); + return this._webContents; + } + +} + type OnBeforeRequestDelegate = (details: OnBeforeRequestDetails) => Promise; -type OnHeadersReceivedDelegate = (details: OnHeadersReceivedDetails) => { cancel: boolean } | undefined; +type OnHeadersReceivedDelegate = (details: OnHeadersReceivedDetails) => { cancel: boolean; } | undefined; class WebviewSession extends Disposable { @@ -47,16 +83,11 @@ class WebviewSession extends Disposable { private readonly _onHeadersReceivedDelegates: Array = []; public constructor( - webview: WebviewTag, + webviewHandle: WebviewTagHandle, ) { super(); - this._register(addDisposableListener(webview, 'did-start-loading', once(() => { - const contents = webview.getWebContents(); - if (!contents) { - return; - } - + this._register(webviewHandle.onFirstLoad(contents => { contents.session.webRequest.onBeforeRequest(async (details, callback) => { for (const delegate of this._onBeforeRequestDelegates) { const result = await delegate(details); @@ -78,7 +109,7 @@ class WebviewSession extends Disposable { } callback({ cancel: false, responseHeaders: details.responseHeaders }); }); - }))); + })); } public onBeforeRequest(delegate: OnBeforeRequestDelegate) { @@ -92,26 +123,19 @@ class WebviewSession extends Disposable { class WebviewProtocolProvider extends Disposable { constructor( - webview: WebviewTag, + handle: WebviewTagHandle, private readonly _getExtensionLocation: () => URI | undefined, private readonly _getLocalResourceRoots: () => ReadonlyArray, private readonly _fileService: IFileService, ) { super(); - this._register(addDisposableListener(webview, 'did-start-loading', once(() => { - const contents = webview.getWebContents(); - if (contents) { - this.registerProtocols(contents); - } - }))); + this._register(handle.onFirstLoad(contents => { + this.registerProtocols(contents); + })); } private registerProtocols(contents: WebContents) { - if (contents.isDestroyed()) { - return; - } - registerFileProtocol(contents, WebviewResourceScheme, this._fileService, this._getExtensionLocation(), () => this._getLocalResourceRoots() ); @@ -143,25 +167,22 @@ class WebviewKeyboardHandler extends Disposable { private _ignoreMenuShortcut = false; constructor( - private readonly _webview: WebviewTag + private readonly _webviewHandle: WebviewTagHandle ) { super(); if (this.shouldToggleMenuShortcutsEnablement) { - this._register(addDisposableListener(this._webview, 'did-start-loading', () => { - const contents = this.getWebContents(); - if (contents) { - contents.on('before-input-event', (_event, input) => { - if (input.type === 'keyDown' && document.activeElement === this._webview) { - this._ignoreMenuShortcut = input.control || input.meta; - this.setIgnoreMenuShortcuts(this._ignoreMenuShortcut); - } - }); - } + this._register(_webviewHandle.onFirstLoad(contents => { + contents.on('before-input-event', (_event, input) => { + if (input.type === 'keyDown' && document.activeElement === this._webviewHandle.webview) { + this._ignoreMenuShortcut = input.control || input.meta; + this.setIgnoreMenuShortcuts(this._ignoreMenuShortcut); + } + }); })); } - this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { + this._register(addDisposableListener(this._webviewHandle.webview, 'ipc-message', (event) => { switch (event.channel) { case 'did-keydown': // Electron: workaround for https://github.com/electron/electron/issues/14258 @@ -189,26 +210,18 @@ class WebviewKeyboardHandler extends Disposable { if (!this.shouldToggleMenuShortcutsEnablement) { return; } - const contents = this.getWebContents(); + const contents = this._webviewHandle.webContents; if (contents) { contents.setIgnoreMenuShortcuts(value); } } - private getWebContents(): WebContents | undefined { - const contents = this._webview.getWebContents(); - if (contents && !contents.isDestroyed()) { - return contents; - } - return undefined; - } - private handleKeydown(event: IKeydownEvent): void { // Create a fake KeyboardEvent from the data provided const emulatedKeyboardEvent = new KeyboardEvent('keydown', event); // Force override the target Object.defineProperty(emulatedKeyboardEvent, 'target', { - get: () => this._webview + get: () => this._webviewHandle.webview }); // And re-dispatch window.dispatchEvent(emulatedKeyboardEvent); @@ -242,11 +255,11 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, constructor( options: WebviewOptions, contentOptions: WebviewContentOptions, + private readonly webviewThemeDataProvider: WebviewThemeDataProvider, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @IFileService fileService: IFileService, @ITunnelService tunnelService: ITunnelService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IEnvironmentService private readonly _environementService: IEnvironmentService, ) { @@ -282,10 +295,12 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, })); }); - const session = this._register(new WebviewSession(this._webview)); + const webviewAndContents = new WebviewTagHandle(this._webview); + + const session = this._register(new WebviewSession(webviewAndContents)); this._register(new WebviewProtocolProvider( - this._webview, + webviewAndContents, () => this.extension ? this.extension.location : undefined, () => (this.content.options.localResourceRoots || []), fileService)); @@ -297,7 +312,7 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, tunnelService, )); - this._register(new WebviewKeyboardHandler(this._webview)); + this._register(new WebviewKeyboardHandler(webviewAndContents)); this._register(addDisposableListener(this._webview, 'console-message', function (e: { level: number; message: string; line: number; sourceId: string; }) { console.log(`[Embedded Page] ${e.message}`); @@ -397,8 +412,8 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, })); } - this.style(themeService.getTheme()); - this._register(themeService.onThemeChange(this.style, this)); + this.style(); + this._register(webviewThemeDataProvider.onThemeDataChanged(this.style, this)); } public mountTo(parent: HTMLElement) { @@ -420,17 +435,13 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, this._webview = undefined; } - if (this._webviewFindWidget) { - this._webviewFindWidget.dispose(); - this._webviewFindWidget = undefined; - } super.dispose(); } private readonly _onDidClickLink = this._register(new Emitter()); public readonly onDidClickLink = this._onDidClickLink.event; - private readonly _onDidScroll = this._register(new Emitter<{ scrollYPercentage: number }>()); + private readonly _onDidScroll = this._register(new Emitter<{ scrollYPercentage: number; }>()); public readonly onDidScroll = this._onDidScroll.event; private readonly _onDidUpdateState = this._register(new Emitter()); @@ -486,18 +497,6 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, this.doUpdateContent(); } - public update(html: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { - if (retainContextWhenHidden && html === this.content.html && areWebviewInputOptionsEqual(options, this.content.options)) { - return; - } - this.content = { - html: html, - options: options, - state: this.content.state, - }; - this.doUpdateContent(); - } - private doUpdateContent() { this._send('content', { contents: this.content.html, @@ -542,7 +541,7 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, } type TelemetryClassification = { - extension?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' } + extension?: { classification: 'SystemMetaData', purpose: 'FeatureInsight'; }; }; type TelemetryData = { extension?: string, @@ -558,34 +557,18 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview, this._send('message', data); } - private style(theme: ITheme): void { - const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService); + private style(): void { + const { styles, activeTheme } = this.webviewThemeDataProvider.getWebviewThemeData(); this._send('styles', { styles, activeTheme }); if (this._webviewFindWidget) { - this._webviewFindWidget.updateTheme(theme); + this._webviewFindWidget.updateTheme(this.webviewThemeDataProvider.getTheme()); } } public layout(): void { - if (!this._webview || this._webview.style.width === '0px') { - return; - } - const contents = this._webview.getWebContents(); - if (!contents || contents.isDestroyed()) { - return; - } - const window = (contents as any).getOwnerBrowserWindow(); - if (!window || !window.webContents || window.webContents.isDestroyed()) { - return; - } - window.webContents.getZoomFactor((factor: number) => { - if (contents.isDestroyed()) { - return; - } + // noop - contents.setZoomFactor(factor); - }); } private readonly _hasFindResult = this._register(new Emitter()); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts index 49ec194e743..3c2c88c4c26 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts @@ -6,17 +6,22 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay'; -import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export class ElectronWebviewService implements IWebviewService { _serviceBrand: undefined; + private readonly webviewThemeDataProvider: WebviewThemeDataProvider; + constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private readonly _configService: IConfigurationService, - ) { } + ) { + this.webviewThemeDataProvider = this._instantiationService.createInstance(WebviewThemeDataProvider); + } createWebview( id: string, @@ -25,9 +30,9 @@ export class ElectronWebviewService implements IWebviewService { ): WebviewElement { const useExternalEndpoint = this._configService.getValue('webview.experimental.useExternalEndpoint'); if (useExternalEndpoint) { - return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions); + return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, this.webviewThemeDataProvider); } else { - return this._instantiationService.createInstance(ElectronWebviewBasedWebview, options, contentOptions); + return this._instantiationService.createInstance(ElectronWebviewBasedWebview, options, contentOptions, this.webviewThemeDataProvider); } } diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/openWebsite.ts b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/openWebsite.ts deleted file mode 100644 index 7eb2bc0dc1c..00000000000 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/openWebsite.ts +++ /dev/null @@ -1,72 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import * as platform from 'vs/base/common/platform'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; -import { IProductService } from 'vs/platform/product/common/productService'; - -export class OpenWelcomePageInBrowser implements IWorkbenchContribution { - - private static readonly hideWelcomeSettingskey = 'workbench.hide.welcome'; - - private welcomePageURL?: string; - private appName: string; - - constructor( - @IStorageService private readonly storageService: IStorageService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService productService: IProductService - ) { - this.appName = productService.nameLong; - this.welcomePageURL = productService.welcomePage; - - if ( - !productService.welcomePage || - environmentService.skipGettingStarted || - environmentService.isExtensionDevelopment - ) { - return; - } - - this.handleWelcome(); - } - - private getUrl(telemetryInfo: ITelemetryInfo): string { - return `${this.welcomePageURL}&&from=${this.appName}&&id=${telemetryInfo.machineId}`; - } - - private openExternal(url: string) { - // Don't open the welcome page as the root user on Linux, this is due to a bug with xdg-open - // which recommends against running itself as root. - if (platform.isLinux && platform.isRootUser()) { - return; - } - this.openerService.open(URI.parse(url)); - } - - private handleWelcome(): void { - //make sure the user is online, otherwise refer to the next run to show the welcome page - if (!navigator.onLine) { - return; - } - - let firstStartup = !this.storageService.get(OpenWelcomePageInBrowser.hideWelcomeSettingskey, StorageScope.GLOBAL); - - if (firstStartup && this.welcomePageURL) { - this.telemetryService.getTelemetryInfo().then(info => { - let url = this.getUrl(info); - this.openExternal(url); - this.storageService.store(OpenWelcomePageInBrowser.hideWelcomeSettingskey, true, StorageScope.GLOBAL); - }); - } - } -} diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index 0ba17c87166..d6cbde0d99c 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -222,8 +222,8 @@ class WelcomeOverlay extends Disposable { key.style.bottom = bottom + 'px'; key.style.left = left + 'px'; } else { - key.style.bottom = null; - key.style.left = null; + key.style.bottom = ''; + key.style.left = ''; } } diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index f4a36a39767..b293a6d68b8 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -146,7 +146,6 @@ interface ExtensionSuggestion { const extensionPacks: ExtensionSuggestion[] = [ { name: localize('welcomePage.javaScript', "JavaScript"), id: 'dbaeumer.vscode-eslint' }, - { name: localize('welcomePage.typeScript', "TypeScript"), id: 'ms-vscode.vscode-typescript-tslint-plugin' }, { name: localize('welcomePage.python', "Python"), id: 'ms-python.python' }, // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution.ts similarity index 94% rename from src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts rename to src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution.ts index ab15e8a07ee..d0440e20402 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Registry } from 'vs/platform/registry/common/platform'; -import { BrowserTelemetryOptOut } from 'vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut'; +import { BrowserTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts similarity index 67% rename from src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts rename to src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts index 2d4448c5099..50398d48086 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts @@ -10,7 +10,6 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { IExperimentService, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { language, locale } from 'vs/base/common/platform'; @@ -21,57 +20,60 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution { - private static TELEMETRY_OPT_OUT_SHOWN = 'workbench.telemetryOptOutShown'; + private static readonly TELEMETRY_OPT_OUT_SHOWN = 'workbench.telemetryOptOutShown'; private privacyUrl: string | undefined; constructor( - @IStorageService storageService: IStorageService, - @IOpenerService openerService: IOpenerService, + @IStorageService private readonly storageService: IStorageService, + @IOpenerService private readonly openerService: IOpenerService, @INotificationService private readonly notificationService: INotificationService, - @IHostService hostService: IHostService, + @IHostService private readonly hostService: IHostService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IExperimentService private readonly experimentService: IExperimentService, @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, - @IProductService productService: IProductService - ) { - if (!productService.telemetryOptOutUrl || storageService.get(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, StorageScope.GLOBAL)) { - return; - } - const experimentId = 'telemetryOptOut'; - Promise.all([ - this.getWindowCount(), - experimentService.getExperimentById(experimentId) - ]).then(([count, experimentState]) => { - if (!hostService.hasFocus && count > 1) { - return; + @IProductService private readonly productService: IProductService, + ) { } + + protected async handleTelemetryOptOut(): Promise { + if (this.productService.telemetryOptOutUrl && !this.storageService.get(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, StorageScope.GLOBAL)) { + const experimentId = 'telemetryOptOut'; + + const [count, experimentState] = await Promise.all([this.getWindowCount(), this.experimentService.getExperimentById(experimentId)]); + + if (!this.hostService.hasFocus && count > 1) { + return; // return early if meanwhile another window opened (we only show the opt-out once) } - storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL); - this.privacyUrl = productService.privacyStatementUrl || productService.telemetryOptOutUrl; + this.storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL); - if (experimentState && experimentState.state === ExperimentState.Run && telemetryService.isOptedIn) { + this.privacyUrl = this.productService.privacyStatementUrl || this.productService.telemetryOptOutUrl; + + if (experimentState && experimentState.state === ExperimentState.Run && this.telemetryService.isOptedIn) { this.runExperiment(experimentId); return; } - const telemetryOptOutUrl = productService.telemetryOptOutUrl; + const telemetryOptOutUrl = this.productService.telemetryOptOutUrl; if (telemetryOptOutUrl) { - const optOutNotice = localize('telemetryOptOut.optOutNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt out]({1}).", this.privacyUrl, productService.telemetryOptOutUrl); - const optInNotice = localize('telemetryOptOut.optInNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt in]({1}).", this.privacyUrl, productService.telemetryOptOutUrl); - - notificationService.prompt( - Severity.Info, - telemetryService.isOptedIn ? optOutNotice : optInNotice, - [{ - label: localize('telemetryOptOut.readMore', "Read More"), - run: () => openerService.open(URI.parse(telemetryOptOutUrl)) - }], - { sticky: true } - ); + this.showTelemetryOptOut(telemetryOptOutUrl); } - }) - .then(undefined, onUnexpectedError); + } + } + + private showTelemetryOptOut(telemetryOptOutUrl: string): void { + const optOutNotice = localize('telemetryOptOut.optOutNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt out]({1}).", this.privacyUrl, this.productService.telemetryOptOutUrl); + const optInNotice = localize('telemetryOptOut.optInNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt in]({1}).", this.privacyUrl, this.productService.telemetryOptOutUrl); + + this.notificationService.prompt( + Severity.Info, + this.telemetryService.isOptedIn ? optOutNotice : optInNotice, + [{ + label: localize('telemetryOptOut.readMore', "Read More"), + run: () => this.openerService.open(URI.parse(telemetryOptOutUrl)) + }], + { sticky: true } + ); } protected abstract getWindowCount(): Promise; @@ -98,7 +100,7 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution return this.galleryService.getCoreTranslation(extensionToFetchTranslationsFrom, locale!) .then(translation => { - const translationsFromPack: any = translation && translation.contents ? translation.contents['vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut'] : {}; + const translationsFromPack: any = translation && translation.contents ? translation.contents['vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut'] : {}; if (!!translationsFromPack[promptMessageKey] && !!translationsFromPack[yesLabelKey] && !!translationsFromPack[noLabelKey]) { promptMessage = translationsFromPack[promptMessageKey].replace('{0}', this.privacyUrl) + ' (Please help Microsoft improve Visual Studio Code by allowing the collection of usage data.)'; yesLabel = translationsFromPack[yesLabelKey] + ' (Yes)'; @@ -153,6 +155,22 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut { + constructor( + @IStorageService storageService: IStorageService, + @IOpenerService openerService: IOpenerService, + @INotificationService notificationService: INotificationService, + @IHostService hostService: IHostService, + @ITelemetryService telemetryService: ITelemetryService, + @IExperimentService experimentService: IExperimentService, + @IConfigurationService configurationService: IConfigurationService, + @IExtensionGalleryService galleryService: IExtensionGalleryService, + @IProductService productService: IProductService + ) { + super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService); + + this.handleTelemetryOptOut(); + } + protected async getWindowCount(): Promise { return 1; } diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution.ts similarity index 71% rename from src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts rename to src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution.ts index e634543ae81..44b1e266005 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution.ts @@ -6,8 +6,6 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { NativeTelemetryOptOut } from 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut'; -import { OpenWelcomePageInBrowser } from 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/openWebsite'; +import { NativeTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut'; -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(OpenWelcomePageInBrowser, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeTelemetryOptOut, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.ts similarity index 96% rename from src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts rename to src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.ts index 7c6cb2bfb6e..d561cf83320 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.ts @@ -12,7 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IProductService } from 'vs/platform/product/common/productService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut'; +import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut'; import { IElectronService } from 'vs/platform/electron/node/electron'; export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { @@ -30,6 +30,8 @@ export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { @IElectronService private readonly electronService: IElectronService ) { super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService); + + this.handleTelemetryOptOut(); } protected getWindowCount(): Promise { diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts index 6d69a2758f1..2b7a9668bd2 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts @@ -10,6 +10,7 @@ import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as marked from 'vs/base/common/marked/marked'; import { Schemas } from 'vs/base/common/network'; +import { isEqual } from 'vs/base/common/resources'; export class WalkThroughModel extends EditorModel { @@ -78,7 +79,7 @@ export class WalkThroughInput extends EditorInput { return this.options.telemetryFrom; } - getTelemetryDescriptor(): { [key: string]: unknown } { + getTelemetryDescriptor(): { [key: string]: unknown; } { const descriptor = super.getTelemetryDescriptor(); descriptor['target'] = this.getTelemetryFrom(); /* __GDPR__FRAGMENT__ @@ -130,7 +131,7 @@ export class WalkThroughInput extends EditorInput { let otherResourceEditorInput = otherInput; // Compare by properties - return otherResourceEditorInput.options.resource.toString() === this.options.resource.toString(); + return isEqual(otherResourceEditorInput.options.resource, this.options.resource); } return false; diff --git a/src/vs/workbench/electron-browser/actions/developerActions.ts b/src/vs/workbench/electron-browser/actions/developerActions.ts index d9b50a3f494..e4c1160ca47 100644 --- a/src/vs/workbench/electron-browser/actions/developerActions.ts +++ b/src/vs/workbench/electron-browser/actions/developerActions.ts @@ -7,13 +7,19 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class ToggleDevToolsAction extends Action { static readonly ID = 'workbench.action.toggleDevTools'; - static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); + static readonly LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); - constructor(id: string, label: string, @IElectronService private readonly electronService: IElectronService) { + constructor( + id: string, + label: string, + @IElectronService private readonly electronService: IElectronService + ) { super(id, label); } @@ -25,9 +31,13 @@ export class ToggleDevToolsAction extends Action { export class ToggleSharedProcessAction extends Action { static readonly ID = 'workbench.action.toggleSharedProcess'; - static LABEL = nls.localize('toggleSharedProcess', "Toggle Shared Process"); + static readonly LABEL = nls.localize('toggleSharedProcess', "Toggle Shared Process"); - constructor(id: string, label: string, @ISharedProcessService private readonly sharedProcessService: ISharedProcessService) { + constructor( + id: string, + label: string, + @ISharedProcessService private readonly sharedProcessService: ISharedProcessService + ) { super(id, label); } @@ -35,3 +45,22 @@ export class ToggleSharedProcessAction extends Action { return this.sharedProcessService.toggleSharedProcessWindow(); } } + +export class ConfigureRuntimeArgumentsAction extends Action { + + static readonly ID = 'workbench.action.configureRuntimeArguments'; + static readonly LABEL = nls.localize('configureRuntimeArguments', "Configure Runtime Arguments"); + + constructor( + id: string, + label: string, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEditorService private readonly editorService: IEditorService + ) { + super(id, label); + } + + async run(): Promise { + await this.editorService.openEditor({ resource: this.environmentService.argvResource }); + } +} diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 9e82222446e..9bc18a02043 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -137,10 +137,10 @@ export class ZoomResetAction extends BaseZoomAction { } } -export class RestartWithExtensionsDisabledAction extends Action { +export class ReloadWindowWithExtensionsDisabledAction extends Action { - static readonly ID = 'workbench.action.restartWithExtensionsDisabled'; - static LABEL = nls.localize('restartWithExtensionsDisabled', "Restart With Extensions Disabled"); + static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled'; + static readonly LABEL = nls.localize('reloadWindowWithExtensionsDisabled', "Reload With Extensions Disabled"); constructor( id: string, @@ -151,7 +151,7 @@ export class RestartWithExtensionsDisabledAction extends Action { } async run(): Promise { - await this.electronService.relaunch({ addArgs: ['--disable-extensions'] }); + await this.electronService.reload({ disableExtensions: true }); return true; } @@ -217,7 +217,7 @@ export abstract class BaseSwitchWindow extends Action { export class SwitchWindow extends BaseSwitchWindow { static readonly ID = 'workbench.action.switchWindow'; - static LABEL = nls.localize('switchWindow', "Switch Window..."); + static readonly LABEL = nls.localize('switchWindow', "Switch Window..."); constructor( id: string, @@ -240,7 +240,7 @@ export class SwitchWindow extends BaseSwitchWindow { export class QuickSwitchWindow extends BaseSwitchWindow { static readonly ID = 'workbench.action.quickSwitchWindow'; - static LABEL = nls.localize('quickSwitchWindow', "Quick Switch Window..."); + static readonly LABEL = nls.localize('quickSwitchWindow', "Quick Switch Window..."); constructor( id: string, diff --git a/src/vs/workbench/electron-browser/actions/workspaceActions.ts b/src/vs/workbench/electron-browser/actions/workspaceActions.ts index ca7bcfab229..6647ac84756 100644 --- a/src/vs/workbench/electron-browser/actions/workspaceActions.ts +++ b/src/vs/workbench/electron-browser/actions/workspaceActions.ts @@ -14,7 +14,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ export class SaveWorkspaceAsAction extends Action { static readonly ID = 'workbench.action.saveWorkspaceAs'; - static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As..."); + static readonly LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As..."); constructor( id: string, diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 94a1259ed28..b1398d6415f 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -11,8 +11,8 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { ToggleSharedProcessAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; -import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, QuickSwitchWindow, RestartWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; +import { ToggleSharedProcessAction, ToggleDevToolsAction, ConfigureRuntimeArgumentsAction } from 'vs/workbench/electron-browser/actions/developerActions'; +import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, QuickSwitchWindow, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; import { SaveWorkspaceAsAction, DuplicateWorkspaceInNewWindowAction } from 'vs/workbench/electron-browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -21,6 +21,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext } from 'vs/workbench/browser/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; // Actions (function registerActions(): void { @@ -98,7 +99,8 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; (function registerDeveloperActions(): void { const developerCategory = nls.localize('developer', "Developer"); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(RestartWithExtensionsDisabledAction, RestartWithExtensionsDisabledAction.ID, RestartWithExtensionsDisabledAction.LABEL), 'Developer: Restart With Extensions Disabled', developerCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureRuntimeArgumentsAction, ConfigureRuntimeArgumentsAction.ID, ConfigureRuntimeArgumentsAction.LABEL), 'Developer: Configure Runtime Arguments', developerCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Developer: Reload With Extensions Disabled', developerCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL), 'Developer: Toggle Developer Tools', developerCategory); KeybindingsRegistry.registerKeybindingRule({ @@ -306,7 +308,7 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; 'default': false, 'scope': ConfigurationScope.APPLICATION, 'description': nls.localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured."), - 'included': isMacintosh && parseFloat(os.release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) + 'included': isMacintosh && parseFloat(os.release()) >= 17 // Minimum: macOS Sierra (10.13.x = darwin 17.x) }, 'window.nativeFullScreen': { 'type': 'boolean', @@ -341,3 +343,32 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; } }); })(); + +// JSON Schemas +(function registerJSONSchemas(): void { + const argvDefinitionFileSchemaId = 'vscode://schemas/argv'; + const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); + + jsonRegistry.registerSchema(argvDefinitionFileSchemaId, { + id: argvDefinitionFileSchemaId, + allowComments: true, + allowTrailingCommas: true, + description: 'VSCode static command line definition file', + type: 'object', + additionalProperties: false, + properties: { + locale: { + type: 'string', + description: nls.localize('argv.locale', 'The display Language to use. Picking a different language requires the associated language pack to be installed.') + }, + 'disable-hardware-acceleration': { + type: 'boolean', + description: nls.localize('argv.disableHardwareAcceleration', 'Disables hardware acceleration. ONLY change this option if you encounter graphic issues.') + }, + 'disable-color-correct-rendering': { + type: 'boolean', + description: nls.localize('argv.disableColorCorrectRendering', 'Resolves issues around color profile selection. ONLY change this option if you encounter graphic issues.') + } + } + }); +})(); diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 3647d409cb7..1e7e3651884 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -50,7 +50,7 @@ import { IUpdateService } from 'vs/platform/update/common/update'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IPreferencesService } from '../services/preferences/common/preferences'; import { IMenubarService, IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/node/menubar'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { IOpenerService, OpenOptions } from 'vs/platform/opener/common/opener'; import { Schemas } from 'vs/base/common/network'; import { IElectronService } from 'vs/platform/electron/node/electron'; @@ -253,7 +253,7 @@ export class ElectronWindow extends Disposable { // Maximize/Restore on doubleclick (for macOS custom title) if (isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { - const titlePart = this.layoutService.getContainer(Parts.TITLEBAR_PART); + const titlePart = assertIsDefined(this.layoutService.getContainer(Parts.TITLEBAR_PART)); this._register(DOM.addDisposableListener(titlePart, DOM.EventType.DBLCLICK, e => { DOM.EventHelper.stop(e); @@ -632,8 +632,8 @@ export class ElectronWindow extends Disposable { await this.lifecycleService.when(LifecyclePhase.Ready); // In diffMode we open 2 resources as diff - if (diffMode && resources.length === 2) { - return this.editorService.openEditor({ leftResource: resources[0].resource!, rightResource: resources[1].resource!, options: { pinned: true } }); + if (diffMode && resources.length === 2 && resources[0].resource && resources[1].resource) { + return this.editorService.openEditor({ leftResource: resources[0].resource, rightResource: resources[1].resource, options: { pinned: true } }); } // For one file, just put it into the current active editor @@ -742,8 +742,8 @@ class NativeMenubarControl extends MenubarControl { const submenu = { items: [] }; if (!this.menus[menuItem.item.submenu]) { - this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); - this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar())); + const menu = this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); + this._register(menu.onDidChange(() => this.updateMenubar())); } const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); diff --git a/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts b/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts index 6962d84f471..623385c0a79 100644 --- a/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts +++ b/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts @@ -11,7 +11,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class NativeClipboardService implements IClipboardService { - private static FILE_FORMAT = 'code/file-list'; // Clipboard format for files + private static readonly FILE_FORMAT = 'code/file-list'; // Clipboard format for files _serviceBrand: undefined; diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index 55ca172c9ae..b74897ff0ed 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -185,7 +185,7 @@ suite('ConfigurationEditingService', () => { test('do not notify error', () => { instantiationService.stub(ITextFileService, 'isDirty', true); const target = sinon.stub(); - instantiationService.stub(INotificationService, { prompt: target, _serviceBrand: undefined, notify: null!, error: null!, info: null!, warn: null!, status: null! }); + instantiationService.stub(INotificationService, { prompt: target, _serviceBrand: undefined, notify: null!, error: null!, info: null!, warn: null!, status: null!, setFilter: null! }); return testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }) .then(() => assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'), (error: ConfigurationEditingError) => { diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 882b085ff97..220b9d988e9 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -25,7 +25,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService { - static INPUT_OR_COMMAND_VARIABLES_PATTERN = /\${((input|command):(.*?))}/g; + static readonly INPUT_OR_COMMAND_VARIABLES_PATTERN = /\${((input|command):(.*?))}/g; constructor( envVariables: IProcessEnvironment, @@ -88,10 +88,10 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR public async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary): Promise { // resolve any non-interactive variables and any contributed variables - config = await this.resolveAny(folder, config); + config = this.resolveAny(folder, config); // resolve input variables in the order in which they are encountered - return this.resolveWithInteraction(folder, config, section, variables, true).then(mapping => { + return this.resolveWithInteraction(folder, config, section, variables).then(mapping => { // finally substitute evaluated command variables (if there are any) if (!mapping) { return null; @@ -103,7 +103,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR }); } - public async resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary, skipContributed: boolean = false): Promise | undefined> { + public async resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary): Promise | undefined> { // resolve any non-interactive variables and any contributed variables const resolved = await this.resolveAnyMap(folder, config); config = resolved.newConfig; @@ -180,6 +180,11 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR throw new Error(nls.localize('commandVariable.noStringType', "Cannot substitute command variable '{0}' because command did not return a result of type string.", commandId)); } break; + default: + // Try to resolve it as a contributed variable + if (this._contributedVariables.has(variable)) { + result = await this._contributedVariables.get(variable)!(); + } } if (typeof result === 'string') { @@ -208,6 +213,11 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR } } } + this._contributedVariables.forEach((value, contributed: string) => { + if ((variables.indexOf(contributed) < 0) && (object.indexOf('${' + contributed + '}') >= 0)) { + variables.push(contributed); + } + }); } else if (Types.isArray(object)) { object.forEach(value => { this.findVariables(value, variables); diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index ea98be3d578..df0da9e454f 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -20,7 +20,7 @@ export interface IConfigurationResolverService { * Recursively resolves all variables in the given config and returns a copy of it with substituted values. * Command variables are only substituted if a "commandValueMapping" dictionary is given and if it contains an entry for the command. */ - resolveAny(folder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): Promise; + resolveAny(folder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): any; /** * Recursively resolves all variables (including commands and user input) in the given config and returns a copy of it with substituted values. diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 469a3ea58b8..03f17d054c8 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -27,12 +27,12 @@ export interface IVariableResolveContext { export class AbstractVariableResolverService implements IConfigurationResolverService { - static VARIABLE_REGEXP = /\$\{(.*?)\}/g; - static VARIABLE_REGEXP_SINGLE = /\$\{(.*?)\}/; + static readonly VARIABLE_REGEXP = /\$\{(.*?)\}/g; + static readonly VARIABLE_REGEXP_SINGLE = /\$\{(.*?)\}/; _serviceBrand: undefined; - private _contributedVariables: Map Promise> = new Map(); + protected _contributedVariables: Map Promise> = new Map(); constructor( private _context: IVariableResolveContext, @@ -53,7 +53,8 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return this.recursiveResolve(root ? root.uri : undefined, value); } - private async resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): Promise { + public resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): any { + const result = objects.deepClone(config) as any; // hoist platform specific attributes to top level @@ -71,16 +72,16 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe delete result.linux; // substitute all variables recursively in string values - return this.recursiveResolvePromise(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables); + return this.recursiveResolve(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables); } - public resolveAny(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): Promise { + public resolveAny(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): any { return this.resolveAnyBase(workspaceFolder, config, commandValueMapping); } - protected async resolveAnyMap(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): Promise<{ newConfig: any, resolvedVariables: Map }> { + public resolveAnyMap(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): { newConfig: any, resolvedVariables: Map } { const resolvedVariables = new Map(); - const newConfig = await this.resolveAnyBase(workspaceFolder, config, commandValueMapping, resolvedVariables); + const newConfig = this.resolveAnyBase(workspaceFolder, config, commandValueMapping, resolvedVariables); return { newConfig, resolvedVariables }; } @@ -116,23 +117,6 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return value; } - private async recursiveResolvePromise(folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): Promise { - if (types.isString(value)) { - return this.resolveStringPromise(folderUri, value, commandValueMapping, resolvedVariables); - } else if (types.isArray(value)) { - return Promise.all(value.map(s => this.recursiveResolvePromise(folderUri, s, commandValueMapping, resolvedVariables))); - } else if (types.isObject(value)) { - let result: IStringDictionary | string[]> = Object.create(null); - const keys = Object.keys(value); - for (let key of keys) { - const replaced = await this.resolveStringPromise(folderUri, key, commandValueMapping, resolvedVariables); - result[replaced] = await this.recursiveResolvePromise(folderUri, value[key], commandValueMapping, resolvedVariables); - } - return result; - } - return value; - } - private resolveString(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary | undefined, resolvedVariables?: Map): string { // loop through all variables occurrences in 'value' @@ -150,37 +134,6 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return replaced; } - private async resolveStringPromise(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary | undefined, resolvedVariables?: Map): Promise { - // loop through all variables occurrences in 'value' - const matches = value.match(AbstractVariableResolverService.VARIABLE_REGEXP); - const replaces: Map = new Map(); - if (!matches) { - return value; - } - for (const match of matches) { - const evaluate = await this.evaluateSingleContributedVariable(match, match.match(AbstractVariableResolverService.VARIABLE_REGEXP_SINGLE)![1]); - if (evaluate !== match) { - replaces.set(match, evaluate); - } - } - - const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => { - - let resolvedValue = this.evaluateSingleVariable(match, variable, folderUri, commandValueMapping); - if ((resolvedValue === match) && (replaces.has(match))) { - resolvedValue = replaces.get(match)!; - } - - if (resolvedVariables) { - resolvedVariables.set(variable, resolvedValue); - } - - return resolvedValue; - }); - - return replaced; - } - private evaluateSingleVariable(match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary | undefined): string { // try to separate variable arguments from variable name @@ -323,25 +276,15 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return match; default: - return match; + return this.resolveFromMap(match, variable, commandValueMapping, undefined); } } } } - private async evaluateSingleContributedVariable(match: string, variable: string): Promise { - if (this._contributedVariables.has(variable)) { - const contributedValue: string | undefined = await this._contributedVariables.get(variable)!(); - if (contributedValue !== undefined) { - return contributedValue; - } - } - return match; - } - - private resolveFromMap(match: string, argument: string | undefined, commandValueMapping: IStringDictionary | undefined, prefix: string): string { + private resolveFromMap(match: string, argument: string | undefined, commandValueMapping: IStringDictionary | undefined, prefix: string | undefined): string { if (argument && commandValueMapping) { - const v = commandValueMapping[prefix + ':' + argument]; + const v = (prefix === undefined) ? commandValueMapping[argument] : commandValueMapping[prefix + ':' + argument]; if (typeof v === 'string') { return v; } diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index fe86b65a202..04c50e16b17 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -494,7 +494,7 @@ suite('Configuration Resolver Service', () => { 'name': '${' + variable + '}', }; configurationResolverService!.contributeVariable(variable, async () => { return buildTask; }); - return configurationResolverService!.resolveAny(workspace, configuration).then(result => { + return configurationResolverService!.resolveWithInteractionReplace(workspace, configuration).then(result => { assert.deepEqual(result, { 'name': `${buildTask}` }); diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts index 20946f64ae1..d50c058d3aa 100644 --- a/src/vs/workbench/services/credentials/browser/credentialsService.ts +++ b/src/vs/workbench/services/credentials/browser/credentialsService.ts @@ -6,6 +6,7 @@ import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { find } from 'vs/base/common/arrays'; export interface ICredentialsProvider { getPassword(service: string, account: string): Promise; @@ -14,7 +15,7 @@ export interface ICredentialsProvider { deletePassword(service: string, account: string): Promise; findPassword(service: string): Promise; - findCredentials(service: string): Promise>; + findCredentials(service: string): Promise>; } export class BrowserCredentialsService implements ICredentialsService { @@ -47,7 +48,7 @@ export class BrowserCredentialsService implements ICredentialsService { return this.credentialsProvider.findPassword(service); } - findCredentials(service: string): Promise> { + findCredentials(service: string): Promise> { return this.credentialsProvider.findCredentials(service); } } @@ -88,17 +89,12 @@ class InMemoryCredentialsProvider implements ICredentialsProvider { return credential ? credential.password : null; } - private doFindPassword(service: string, account?: string): ICredential | null { - for (const credential of this.credentials) { - if (credential.service === service && (typeof account !== 'string' || credential.account === account)) { - return credential; - } - } - - return null; + private doFindPassword(service: string, account?: string): ICredential | undefined { + return find(this.credentials, credential => + credential.service === service && (typeof account !== 'string' || credential.account === account)); } - async findCredentials(service: string): Promise> { + async findCredentials(service: string): Promise> { return this.credentials .filter(credential => credential.service === service) .map(({ account, password }) => ({ account, password })); diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index a10d68678a5..6e7d60942b4 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { IDecorationsService, IDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IDecorationData } from './decorations'; import { TernarySearchTree } from 'vs/base/common/map'; -import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { isThenable } from 'vs/base/common/async'; import { LinkedList } from 'vs/base/common/linkedList'; import { createStyleSheet, createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom'; @@ -19,6 +19,7 @@ import { localize } from 'vs/nls'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; class DecorationRule { @@ -75,9 +76,10 @@ class DecorationRule { } // bubble badge + // TODO @misolori update bubble badge to use class name instead of unicode createCSSRule( `.${this.bubbleBadgeClassName}::after`, - `content: "\uf052"; color: ${getColor(theme, color)}; font-family: octicons; font-size: 14px; padding-right: 14px; opacity: 0.4;`, + `content: "\uf052"; color: ${getColor(theme, color)}; font-family: octicons; font-size: 14px; padding-right: 10px; opacity: 0.4;`, element ); } @@ -95,25 +97,21 @@ class DecorationRule { } } -class DecorationStyles extends Disposable { +class DecorationStyles { private readonly _styleElement = createStyleSheet(); private readonly _decorationRules = new Map(); + private readonly _dispoables = new DisposableStore(); constructor( private _themeService: IThemeService, ) { - super(); - this._register(this._themeService.onThemeChange(this._onThemeChange, this)); + this._themeService.onThemeChange(this._onThemeChange, this, this._dispoables); } dispose(): void { - super.dispose(); - - const parent = this._styleElement.parentElement; - if (parent) { - parent.removeChild(this._styleElement); - } + this._dispoables.dispose(); + this._styleElement.remove(); } asDecoration(data: IDecorationData[], onlyChildren: boolean): IDecoration { @@ -171,7 +169,7 @@ class DecorationStyles extends Disposable { if (value.isUnused()) { let remove: boolean = false; if (Array.isArray(data)) { - remove = data.some(data => !usedDecorations.has(DecorationRule.keyOf(data))); + remove = data.every(data => !usedDecorations.has(DecorationRule.keyOf(data))); } else if (!usedDecorations.has(DecorationRule.keyOf(data))) { remove = true; } @@ -223,11 +221,11 @@ class DecorationProviderWrapper { private readonly _dispoable: IDisposable; constructor( - private readonly _provider: IDecorationsProvider, + readonly provider: IDecorationsProvider, private readonly _uriEmitter: Emitter, private readonly _flushEmitter: Emitter ) { - this._dispoable = this._provider.onDidChange(uris => { + this._dispoable = this.provider.onDidChange(uris => { if (!uris) { // flush event -> drop all data, can affect everything this.data.clear(); @@ -291,7 +289,7 @@ class DecorationProviderWrapper { } const source = new CancellationTokenSource(); - const dataOrThenable = this._provider.provideDecorations(uri, source.token); + const dataOrThenable = this.provider.provideDecorations(uri, source.token); if (!isThenable | undefined>(dataOrThenable)) { // sync -> we have a result now return this._keepItem(uri, dataOrThenable); @@ -324,7 +322,7 @@ class DecorationProviderWrapper { } } -export class FileDecorationsService implements IDecorationsService { +export class DecorationsService implements IDecorationsService { _serviceBrand: undefined; @@ -332,7 +330,6 @@ export class FileDecorationsService implements IDecorationsService { private readonly _onDidChangeDecorationsDelayed = new Emitter(); private readonly _onDidChangeDecorations = new Emitter(); private readonly _decorationStyles: DecorationStyles; - private readonly _disposables: IDisposable[]; readonly onDidChangeDecorations: Event = Event.any( this._onDidChangeDecorations.event, @@ -344,29 +341,25 @@ export class FileDecorationsService implements IDecorationsService { ); constructor( - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @ILogService private readonly _logService: ILogService, ) { this._decorationStyles = new DecorationStyles(themeService); // every so many events we check if there are // css styles that we don't need anymore let count = 0; - let reg = this.onDidChangeDecorations(() => { + this.onDidChangeDecorations(() => { if (++count % 17 === 0) { this._decorationStyles.cleanUp(this._data.iterator()); } }); - - this._disposables = [ - reg, - this._decorationStyles - ]; } dispose(): void { - dispose(this._disposables); - dispose(this._onDidChangeDecorations); - dispose(this._onDidChangeDecorationsDelayed); + this._decorationStyles.dispose(); + this._onDidChangeDecorations.dispose(); + this._onDidChangeDecorationsDelayed.dispose(); } registerDecorationsProvider(provider: IDecorationsProvider): IDisposable { @@ -396,10 +389,12 @@ export class FileDecorationsService implements IDecorationsService { let data: IDecorationData[] = []; let containsChildren: boolean = false; for (let iter = this._data.iterator(), next = iter.next(); !next.done; next = iter.next()) { + const { label } = next.value.provider; next.value.getOrRetrieve(uri, includeChildren, (deco, isChild) => { if (!isChild || deco.bubble) { data.push(deco); containsChildren = isChild || containsChildren; + this._logService.trace('DecorationsService#getDecoration#getOrRetrieve', label, deco, isChild, uri); } }); } @@ -419,4 +414,4 @@ function getColor(theme: ITheme, color: string | undefined) { return 'inherit'; } -registerSingleton(IDecorationsService, FileDecorationsService); +registerSingleton(IDecorationsService, DecorationsService, true); diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index 6975f0b64d3..2f1298ea85b 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -4,22 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { FileDecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService'; +import { DecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService'; import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ConsoleLogService } from 'vs/platform/log/common/log'; suite('DecorationsService', function () { - let service: FileDecorationsService; + let service: DecorationsService; setup(function () { if (service) { service.dispose(); } - service = new FileDecorationsService(new TestThemeService()); + service = new DecorationsService(new TestThemeService(), new ConsoleLogService()); }); test('Async provider, async/evented result', function () { diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts index 495d0ea725f..f67f9aa064c 100644 --- a/src/vs/workbench/services/dialogs/browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts @@ -129,7 +129,7 @@ export class DialogService implements IDialogService { navigator.userAgent ); - const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail }); + const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail, cancelId: 1 }); if (choice === 0) { this.clipboardService.writeText(detail); diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 62c657356da..e83748f5e23 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -35,6 +35,7 @@ import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { toResource } from 'vs/workbench/common/editor'; +import { normalizeDriveLetter } from 'vs/base/common/labels'; export namespace OpenLocalFileCommand { export const ID = 'workbench.action.files.openLocalFile'; @@ -255,16 +256,6 @@ export class SimpleFileDialog { homedir = resources.dirname(this.options.defaultUri); this.trailing = resources.basename(this.options.defaultUri); } - // append extension - if (isSave && !ext && this.options.filters) { - for (let i = 0; i < this.options.filters.length; i++) { - if (this.options.filters[i].extensions[0] !== '*') { - ext = '.' + this.options.filters[i].extensions[0]; - this.trailing = this.trailing ? this.trailing + ext : ext; - break; - } - } - } } return new Promise(async (resolve) => { @@ -486,7 +477,7 @@ export class SimpleFileDialog { const newPath = this.pathFromUri(item.uri); if (startsWithIgnoreCase(newPath, this.filePickBox.value) && (equalsIgnoreCase(item.label, resources.basename(item.uri)))) { this.filePickBox.valueSelection = [this.pathFromUri(this.currentFolder).length, this.filePickBox.value.length]; - this.insertText(newPath, item.label); + this.insertText(newPath, this.basenameWithTrailingSlash(item.uri)); } else if ((item.label === '..') && startsWithIgnoreCase(this.filePickBox.value, newPath)) { this.filePickBox.valueSelection = [newPath.length, this.filePickBox.value.length]; this.insertText(newPath, ''); @@ -602,7 +593,7 @@ export class SimpleFileDialog { this.autoCompletePathSegment = ''; return false; } - const itemBasename = this.trimTrailingSlash(quickPickItem.label); + const itemBasename = quickPickItem.label; // Either force the autocomplete, or the old value should be one smaller than the new value and match the new value. if (itemBasename === '..') { // Don't match on the up directory item ever. @@ -621,7 +612,7 @@ export class SimpleFileDialog { this.autoCompletePathSegment = ''; this.filePickBox.activeItems = [quickPickItem]; return true; - } else if (force && (!equalsIgnoreCase(quickPickItem.label, (this.userEnteredPathSegment + this.autoCompletePathSegment)))) { + } else if (force && (!equalsIgnoreCase(this.basenameWithTrailingSlash(quickPickItem.uri), (this.userEnteredPathSegment + this.autoCompletePathSegment)))) { this.userEnteredPathSegment = ''; this.autoCompletePathSegment = this.trimTrailingSlash(itemBasename); this.activeItem = quickPickItem; @@ -807,7 +798,7 @@ export class SimpleFileDialog { } private pathFromUri(uri: URI, endWithSeparator: boolean = false): string { - let result: string = uri.fsPath.replace(/\n/g, ''); + let result: string = normalizeDriveLetter(uri.fsPath).replace(/\n/g, ''); if (this.separator === '/') { result = result.replace(/\\/g, this.separator); } else { @@ -848,7 +839,7 @@ export class SimpleFileDialog { } private createBackItem(currFolder: URI): FileQuickPickItem | null { - const parentFolder = resources.dirname(currFolder)!; + const parentFolder = resources.dirname(currFolder); if (!resources.isEqual(currFolder, parentFolder, true)) { return { label: '..', uri: resources.addTrailingPathSeparator(parentFolder, this.separator), isFolder: true }; } @@ -913,7 +904,7 @@ export class SimpleFileDialog { try { const stat = await this.fileService.resolve(fullPath); if (stat.isDirectory) { - filename = this.basenameWithTrailingSlash(fullPath); + filename = resources.basename(fullPath); fullPath = resources.addTrailingPathSeparator(fullPath, this.separator); return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; } else if (!stat.isDirectory && this.allowFileSelection && this.filterFile(fullPath)) { diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 3c564000389..43caf27af2e 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -43,10 +43,11 @@ export class DialogService implements IDialogService { _serviceBrand: undefined; - private impl: IDialogService; + private nativeImpl: IDialogService; + private customImpl: IDialogService; constructor( - @IConfigurationService configurationService: IConfigurationService, + @IConfigurationService private configurationService: IConfigurationService, @ILogService logService: ILogService, @ILayoutService layoutService: ILayoutService, @IThemeService themeService: IThemeService, @@ -56,27 +57,32 @@ export class DialogService implements IDialogService { @IClipboardService clipboardService: IClipboardService, @IElectronService electronService: IElectronService ) { + this.customImpl = new HTMLDialogService(logService, layoutService, themeService, keybindingService, productService, clipboardService); + this.nativeImpl = new NativeDialogService(logService, sharedProcessService, electronService, clipboardService); + } - // Use HTML based dialogs - if (configurationService.getValue('workbench.dialogs.customEnabled') === true) { - this.impl = new HTMLDialogService(logService, layoutService, themeService, keybindingService, productService, clipboardService); - } - // Electron dialog service - else { - this.impl = new NativeDialogService(logService, sharedProcessService, electronService, clipboardService); - } + private get useCustomDialog(): boolean { + return this.configurationService.getValue('workbench.dialogs.customEnabled') === true; } confirm(confirmation: IConfirmation): Promise { - return this.impl.confirm(confirmation); + if (this.useCustomDialog) { + return this.customImpl.confirm(confirmation); + } + + return this.nativeImpl.confirm(confirmation); } show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions | undefined): Promise { - return this.impl.show(severity, message, buttons, options); + if (this.useCustomDialog) { + return this.customImpl.show(severity, message, buttons, options); + } + + return this.nativeImpl.show(severity, message, buttons, options); } about(): Promise { - return this.impl.about(); + return this.nativeImpl.about(); } } diff --git a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts index 44790f68ce5..a4873580a8d 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts @@ -46,8 +46,8 @@ export class FileDialogService extends AbstractFileDialogService implements IFil private shouldUseSimplified(schema: string): { useSimplified: boolean, isSetting: boolean } { const setting = (this.configurationService.getValue('files.simpleDialog.enable') === true); - - return { useSimplified: (schema !== Schemas.file) || setting, isSetting: (schema === Schemas.file) && setting }; + const newWindowSetting = (this.configurationService.getValue('window.openFilesInNewWindow') === 'on'); + return { useSimplified: (schema !== Schemas.file) || setting, isSetting: newWindowSetting }; } async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { @@ -110,7 +110,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil return this.pickFileToSaveSimplified(schema, options); } else { const result = await this.electronService.showSaveDialog(this.toNativeSaveDialogOptions(options)); - if (result && result.filePath) { + if (result && !result.canceled && result.filePath) { return URI.file(result.filePath); } } @@ -134,7 +134,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil } const result = await this.electronService.showSaveDialog(this.toNativeSaveDialogOptions(options)); - if (result && result.filePath) { + if (result && !result.canceled && result.filePath) { return URI.file(result.filePath); } @@ -149,7 +149,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const defaultUri = options.defaultUri; - const newOptions: OpenDialogOptions = { + const newOptions: OpenDialogOptions & { properties: string[] } = { title: options.title, defaultPath: defaultUri && defaultUri.fsPath, buttonLabel: options.openLabel, @@ -157,18 +157,18 @@ export class FileDialogService extends AbstractFileDialogService implements IFil properties: [] }; - newOptions.properties!.push('createDirectory'); + newOptions.properties.push('createDirectory'); if (options.canSelectFiles) { - newOptions.properties!.push('openFile'); + newOptions.properties.push('openFile'); } if (options.canSelectFolders) { - newOptions.properties!.push('openDirectory'); + newOptions.properties.push('openDirectory'); } if (options.canSelectMany) { - newOptions.properties!.push('multiSelections'); + newOptions.properties.push('multiSelections'); } const result = await this.electronService.showOpenDialog(newOptions); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index a056de4bf9d..99b5ff364d3 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -15,7 +15,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { basename } from 'vs/base/common/resources'; +import { basename, isEqual } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { localize } from 'vs/nls'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -152,8 +152,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { for (const handler of this.openEditorHandlers) { const result = handler(event.editor, event.options, group); - if (result && result.override) { - event.prevent((() => result.override!.then(editor => withNullAsUndefined(editor)))); + const override = result ? result.override : undefined; + if (override) { + event.prevent((() => override.then(editor => withNullAsUndefined(editor)))); break; } } @@ -464,7 +465,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } const resourceInput = editor as IResourceInput | IUntitledResourceInput; - if (resourceInput.resource && resource.toString() === resourceInput.resource.toString()) { + if (resourceInput.resource && isEqual(resource, resourceInput.resource)) { return editorInGroup; } } diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 54096dfc116..98e93a57adb 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -43,7 +43,7 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { resolve(): Promise { return Promise.resolve(null); } matches(other: TestEditorInput): boolean { return other && this.resource.toString() === other.resource.toString() && other instanceof TestEditorInput; } setEncoding(encoding: string) { } - getEncoding(): string { return null!; } + getEncoding() { return undefined; } setPreferredEncoding(encoding: string) { } setMode(mode: string) { } setPreferredMode(mode: string) { } diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index f3c781424b3..9601ae63122 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -57,7 +57,7 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { resolve(): Promise { return !this.fails ? Promise.resolve(null) : Promise.reject(new Error('fails')); } matches(other: TestEditorInput): boolean { return other && other.resource && this.resource.toString() === other.resource.toString() && other instanceof TestEditorInput; } setEncoding(encoding: string) { } - getEncoding(): string { return null!; } + getEncoding() { return undefined; } setPreferredEncoding(encoding: string) { } setMode(mode: string) { } setPreferredMode(mode: string) { } diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index ee56e84cdc4..b85732fef57 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -88,7 +88,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment this.userDataSyncLogResource = joinPath(options.logsPath, 'userDataSync.log'); this.keybindingsResource = joinPath(this.userRoamingDataHome, 'keybindings.json'); this.keyboardLayoutResource = joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); - this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json'); + this.argvResource = joinPath(this.userRoamingDataHome, 'argv.json'); this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS); this.configuration.backupWorkspaceResource = joinPath(this.backupHome, options.workspaceId); this.configuration.connectionToken = options.connectionToken || getCookieValue('vscode-tkn'); @@ -145,7 +145,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment settingsResource: URI; keybindingsResource: URI; keyboardLayoutResource: URI; - localeResource: URI; + argvResource: URI; settingsSyncPreviewResource: URI; userDataSyncLogResource: URI; machineSettingsHome: URI; @@ -170,7 +170,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment log?: string; logsPath: string; verbose: boolean; - skipGettingStarted: boolean; skipReleaseNotes: boolean; mainIPCHandle: string; sharedIPCHandle: string; diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index b19a45caf00..0f7a4d8e59a 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -28,7 +28,6 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly webviewResourceRoot: string; readonly webviewCspSource: string; - readonly skipGettingStarted: boolean | undefined; readonly skipReleaseNotes: boolean | undefined; } diff --git a/src/vs/workbench/services/environment/node/environmentService.ts b/src/vs/workbench/services/environment/node/environmentService.ts index 407fbe12c18..86732170c5f 100644 --- a/src/vs/workbench/services/environment/node/environmentService.ts +++ b/src/vs/workbench/services/environment/node/environmentService.ts @@ -36,8 +36,6 @@ export class WorkbenchEnvironmentService extends EnvironmentService implements I this.configuration.backupWorkspaceResource = this.configuration.backupPath ? toBackupWorkspaceResource(this.configuration.backupPath, this) : undefined; } - get skipGettingStarted(): boolean { return !!this.args['skip-getting-started']; } - get skipReleaseNotes(): boolean { return !!this.args['skip-release-notes']; } @memoize diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index cbed6dcbbd9..8708f30a37c 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionIdentifier, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -43,7 +43,6 @@ export class ExtensionEnablementService extends Disposable implements IExtension super(); this.storageManger = this._register(new StorageManager(storageService)); this._register(this.storageManger.onDidChange(extensions => this.onDidChangeStorage(extensions))); - this._register(extensionManagementService.onDidInstallExtension(this._onDidInstallExtension, this)); this._register(extensionManagementService.onDidUninstallExtension(this._onDidUninstallExtension, this)); } @@ -286,16 +285,6 @@ export class ExtensionEnablementService extends Disposable implements IExtension this._onEnablementChanged.fire(extensions); } - private _onDidInstallExtension(event: DidInstallExtensionEvent): void { - if (event.local && event.operation === InstallOperation.Install) { - const wasDisabled = !this.isEnabled(event.local); - this._reset(event.local.identifier); - if (wasDisabled) { - this._onEnablementChanged.fire([event.local]); - } - } - } - private _onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void { if (!error) { this._reset(identifier); diff --git a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index 88955149380..93bc95313b8 100644 --- a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionEnablementService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -71,12 +71,11 @@ suite('ExtensionEnablementService Test', () => { let testObject: IExtensionEnablementService; const didUninstallEvent = new Emitter(); - const didInstallEvent = new Emitter(); setup(() => { instantiationService = new TestInstantiationService(); instantiationService.stub(IConfigurationService, new TestConfigurationService()); - instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, onDidInstallExtension: didInstallEvent.event, getInstalled: () => Promise.resolve([] as ILocalExtension[]) } as IExtensionManagementService); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => Promise.resolve([] as ILocalExtension[]) } as IExtensionManagementService); instantiationService.stub(IExtensionManagementServerService, { localExtensionManagementServer: { extensionManagementService: instantiationService.get(IExtensionManagementService) @@ -338,90 +337,6 @@ suite('ExtensionEnablementService Test', () => { assert.equal(testObject.getEnablementState(extension), EnablementState.EnabledGlobally); }); - test('test installing an extension re-eanbles it when disabled globally', async () => { - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledGlobally); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); - assert.ok(testObject.isEnabled(local)); - assert.equal(testObject.getEnablementState(local), EnablementState.EnabledGlobally); - }); - - test('test updating an extension does not re-eanbles it when disabled globally', async () => { - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledGlobally); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); - assert.ok(!testObject.isEnabled(local)); - assert.equal(testObject.getEnablementState(local), EnablementState.DisabledGlobally); - }); - - test('test installing an extension fires enablement change event when disabled globally', async () => { - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledGlobally); - return new Promise((c, e) => { - testObject.onEnablementChanged(([e]) => { - if (e.identifier.id === local.identifier.id) { - c(); - } - }); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); - }); - }); - - test('test updating an extension does not fires enablement change event when disabled globally', async () => { - const target = sinon.spy(); - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledGlobally); - testObject.onEnablementChanged(target); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); - assert.ok(!target.called); - }); - - test('test installing an extension re-eanbles it when workspace disabled', async () => { - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledWorkspace); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); - assert.ok(testObject.isEnabled(local)); - assert.equal(testObject.getEnablementState(local), EnablementState.EnabledGlobally); - }); - - test('test updating an extension does not re-eanbles it when workspace disabled', async () => { - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledWorkspace); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); - assert.ok(!testObject.isEnabled(local)); - assert.equal(testObject.getEnablementState(local), EnablementState.DisabledWorkspace); - }); - - test('test installing an extension fires enablement change event when workspace disabled', async () => { - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledWorkspace); - return new Promise((c, e) => { - testObject.onEnablementChanged(([e]) => { - if (e.identifier.id === local.identifier.id) { - c(); - } - }); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); - }); - }); - - test('test updating an extension does not fires enablement change event when workspace disabled', async () => { - const target = sinon.spy(); - const local = aLocalExtension('pub.a'); - await testObject.setEnablement([local], EnablementState.DisabledWorkspace); - testObject.onEnablementChanged(target); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); - assert.ok(!target.called); - }); - - test('test installing an extension should not fire enablement change event when extension is not disabled', async () => { - const target = sinon.spy(); - const local = aLocalExtension('pub.a'); - testObject.onEnablementChanged(target); - didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); - assert.ok(!target.called); - }); - test('test remove an extension from disablement list when uninstalled', async () => { const extension = aLocalExtension('pub.a'); await testObject.setEnablement([extension], EnablementState.DisabledWorkspace); @@ -480,7 +395,7 @@ suite('ExtensionEnablementService Test', () => { test('test extension is disabled when disabled in enviroment', async () => { const extension = aLocalExtension('pub.a'); instantiationService.stub(IWorkbenchEnvironmentService, { disableExtensions: ['pub.a'] } as IWorkbenchEnvironmentService); - instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, onDidInstallExtension: didInstallEvent.event, getInstalled: () => Promise.resolve([extension, aLocalExtension('pub.b')]) } as IExtensionManagementService); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => Promise.resolve([extension, aLocalExtension('pub.b')]) } as IExtensionManagementService); testObject = new TestExtensionEnablementService(instantiationService); assert.ok(!testObject.isEnabled(extension)); assert.deepEqual(testObject.getEnablementState(extension), EnablementState.DisabledByEnvironemt); diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index c969bd3df55..6a9a536c7a6 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -15,7 +15,7 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; +import { IURLHandler, IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -28,6 +28,7 @@ const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; const URL_TO_HANDLE = 'extensionUrlHandler.urlToHandle'; const CONFIRMED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds'; +const CONFIRMED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions'; function isExtensionId(value: string): boolean { return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value); @@ -74,7 +75,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { const urlToHandleValue = this.storageService.get(URL_TO_HANDLE, StorageScope.WORKSPACE); if (urlToHandleValue) { this.storageService.remove(URL_TO_HANDLE, StorageScope.WORKSPACE); - this.handleURL(URI.revive(JSON.parse(urlToHandleValue)), true); + this.handleURL(URI.revive(JSON.parse(urlToHandleValue)), { trusted: true }); } this.disposable = combinedDisposable( @@ -86,7 +87,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { setTimeout(() => cache.forEach(uri => this.handleURL(uri))); } - async handleURL(uri: URI, confirmed?: boolean): Promise { + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { if (!isExtensionId(uri.authority)) { return false; } @@ -100,12 +101,15 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return true; } - if (!confirmed) { + let showConfirm: boolean; + if (options && options.trusted) { + showConfirm = false; + } else { const confirmedExtensionIds = this.getConfirmedExtensionIds(); - confirmed = confirmedExtensionIds.has(ExtensionIdentifier.toKey(extensionId)); + showConfirm = !confirmedExtensionIds.has(ExtensionIdentifier.toKey(extensionId)); } - if (!confirmed) { + if (showConfirm) { let uriString = uri.toString(); if (uriString.length > 40) { @@ -127,7 +131,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } if (result.checkboxChecked) { - await this.addConfirmedExtensionIdToStorage(extensionId); + this.addConfirmedExtensionIdToStorage(extensionId); } } @@ -136,7 +140,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { if (handler) { if (!wasHandlerAvailable) { // forward it directly - return await handler.handleURL(uri); + return await handler.handleURL(uri, options); } // let the ExtensionUrlHandler instance handle this @@ -289,8 +293,10 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } private getConfirmedExtensionIds(): Set { - const ids = this.getConfirmedExtensionIdsFromConfiguration() - .map(extensionId => ExtensionIdentifier.toKey(extensionId)); + const ids = [ + ...this.getConfirmedExtensionIdsFromStorage(), + ...this.getConfirmedExtensionIdsFromConfiguration(), + ].map(extensionId => ExtensionIdentifier.toKey(extensionId)); return new Set(ids); } @@ -305,12 +311,26 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return confirmedExtensionIds; } - private async addConfirmedExtensionIdToStorage(extensionId: string): Promise { - const confirmedExtensionIds = this.configurationService.getValue>(CONFIRMED_EXTENSIONS_CONFIGURATION_KEY); - const set = new Set(confirmedExtensionIds); - set.add(extensionId); + private getConfirmedExtensionIdsFromStorage(): Array { + const confirmedExtensionIdsJson = this.storageService.get(CONFIRMED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); - await this.configurationService.updateValue(CONFIRMED_EXTENSIONS_CONFIGURATION_KEY, [...set.values()]); + try { + return JSON.parse(confirmedExtensionIdsJson); + } catch (err) { + return []; + } + } + + private addConfirmedExtensionIdToStorage(extensionId: string): void { + const existingConfirmedExtensionIds = this.getConfirmedExtensionIdsFromStorage(); + this.storageService.store( + CONFIRMED_EXTENSIONS_STORAGE_KEY, + JSON.stringify([ + ...existingConfirmedExtensionIds, + ExtensionIdentifier.toKey(extensionId), + ]), + StorageScope.GLOBAL, + ); } dispose(): void { @@ -343,7 +363,7 @@ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandle ExtensionUrlBootstrapHandler.disposable = urlService.registerHandler(this); } - handleURL(uri: URI): Promise { + handleURL(uri: URI, options?: IOpenURLOptions): Promise { ExtensionUrlBootstrapHandler._cache.push(uri); return Promise.resolve(true); } diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 406331cb2bc..e74228ce1dc 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -113,6 +113,10 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { return undefined; } + enableInspectPort(): Promise { + return Promise.resolve(false); + } + private async _createExtHostInitData(): Promise { const [telemetryInfo, extensionDescriptions] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]); const workspace = this._contextService.getWorkspace(); diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 65d4c2f70ac..9833bbfb888 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -256,8 +256,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx return result; } - public getInspectPort(): number { - return 0; + public getInspectPort(_tryEnableInspector: boolean): Promise { + return Promise.resolve(0); } public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise { @@ -459,9 +459,10 @@ class ProposedApiController { // Make enabled proposed API be lowercase for case insensitive comparison this.enableProposedApiFor = (environmentService.args['enable-proposed-api'] || []).map(id => id.toLowerCase()); - this.enableProposedApiForAll = !environmentService.isBuilt || - (!!environmentService.extensionDevelopmentLocationURI && productService.nameLong !== 'Visual Studio Code') || - (this.enableProposedApiFor.length === 0 && 'enable-proposed-api' in environmentService.args); + this.enableProposedApiForAll = + !environmentService.isBuilt || // always allow proposed API when running out of sources + (!!environmentService.extensionDevelopmentLocationURI && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension + (this.enableProposedApiFor.length === 0 && 'enable-proposed-api' in environmentService.args); // always allow proposed API if --enable-proposed-api is provided without extension ID this.productAllowProposedApi = new Set(); if (isNonEmptyArray(productService.extensionAllowedProposedApi)) { diff --git a/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts index 38c4b63eda7..ab3477a4fc7 100644 --- a/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts @@ -118,12 +118,7 @@ export class ExtensionDescriptionRegistry { hasOnlyGoodArcs(id: string, good: Set): boolean { const dependencies = G.getArcs(id); - for (let i = 0; i < dependencies.length; i++) { - if (!good.has(dependencies[i])) { - return false; - } - } - return true; + return dependencies.every(dependency => good.has(dependency)); } getNodes(): string[] { diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts index 3aff4a5e57f..746953e1d39 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts @@ -238,8 +238,11 @@ export class ExtensionHostProcessManager extends Disposable { }); } - public getInspectPort(): number { + public async getInspectPort(tryEnableInspector: boolean): Promise { if (this._extensionHostProcessWorker) { + if (tryEnableInspector) { + await this._extensionHostProcessWorker.enableInspectPort(); + } let port = this._extensionHostProcessWorker.getInspectPort(); if (port) { return port; @@ -248,10 +251,6 @@ export class ExtensionHostProcessManager extends Disposable { return 0; } - public canProfileExtensionHost(): boolean { - return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort()); - } - public async resolveAuthority(remoteAuthority: string): Promise { const authorityPlusIndex = remoteAuthority.indexOf('+'); if (authorityPlusIndex === -1) { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index d68777ae83b..074447cab22 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -89,6 +89,7 @@ export interface IExtensionHostStarter { start(): Promise | null; getInspectPort(): number | undefined; + enableInspectPort(): Promise; dispose(): void; } @@ -212,7 +213,7 @@ export interface IExtensionService { * Return the inspect port or `0`, the latter means inspection * is not possible. */ - getInspectPort(): number; + getInspectPort(tryEnableInspector: boolean): Promise; /** * Restarts the extension host. @@ -270,7 +271,7 @@ export class NullExtensionService implements IExtensionService { getExtension() { return Promise.resolve(undefined); } readExtensionPointContributions(_extPoint: IExtensionPoint): Promise[]> { return Promise.resolve(Object.create(null)); } getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); } - getInspectPort(): number { return 0; } + getInspectPort(_tryEnableInspector: boolean): Promise { return Promise.resolve(0); } restartExtensionHost(): void { } async setRemoteEnvironment(_env: { [key: string]: string | null }): Promise { } canAddExtension(): boolean { return false; } diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index 8dfa1bd4522..83b800cad49 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -225,6 +225,10 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH return undefined; } + enableInspectPort(): Promise { + return Promise.resolve(false); + } + dispose(): void { super.dispose(); diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index ab46c17ec1e..4f5f086a3f9 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -58,7 +58,7 @@ const noop = () => { }; export class RPCProtocol extends Disposable implements IRPCProtocol { - private static UNRESPONSIVE_TIME = 3 * 1000; // 3s + private static readonly UNRESPONSIVE_TIME = 3 * 1000; // 3s private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; @@ -588,12 +588,7 @@ class MessageBuffer { class MessageIO { private static _arrayContainsBuffer(arr: any[]): boolean { - for (let i = 0, len = arr.length; i < len; i++) { - if (arr[i] instanceof VSBuffer) { - return true; - } - } - return false; + return arr.some(value => value instanceof VSBuffer); } public static serializeRequest(req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean, replacer: JSONStringifyReplacer | null): VSBuffer { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 1963444f46f..b7ac24ab84a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -16,7 +16,7 @@ import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IRemoteConsoleLog, log } from 'vs/base/common/console'; import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil'; -import { findFreePort, randomPort } from 'vs/base/node/ports'; +import { findFreePort } from 'vs/base/node/ports'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { generateRandomPipeName, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; @@ -45,6 +45,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { private readonly _onExit: Emitter<[number, string]> = new Emitter<[number, string]>(); public readonly onExit: Event<[number, string]> = this._onExit.event; + private readonly _onDidSetInspectPort = new Emitter(); + private readonly _toDispose = new DisposableStore(); private readonly _isExtensionDevHost: boolean; @@ -127,10 +129,10 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { if (!this._messageProtocol) { this._messageProtocol = Promise.all([ this._tryListenOnPipe(), - !this._environmentService.args['disable-inspect'] ? this._tryFindDebugPort() : Promise.resolve(null) + this._tryFindDebugPort() ]).then(data => { const pipeName = data[0]; - const portData = data[1]; + const portNumber = data[1]; const opts = { env: objects.mixin(objects.deepClone(process.env), { @@ -151,16 +153,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { silent: true }; - if (portData && portData.actual) { + if (portNumber !== 0) { opts.execArgv = [ '--nolazy', - (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portData.actual + (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portNumber ]; - if (!portData.expected) { - // No one asked for 'inspect' or 'inspect-brk', only us. We add another - // option such that the extension host can manipulate the execArgv array - opts.env.VSCODE_PREVENT_FOREIGN_INSPECT = true; - } } const crashReporterOptions = undefined; // TODO@electron pass this in as options to the extension host after verifying this actually works @@ -198,6 +195,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } if (!this._inspectPort) { this._inspectPort = Number(inspectorUrlMatch[2]); + this._onDidSetInspectPort.fire(); } } else { console.group('Extension Host'); @@ -218,11 +216,12 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal)); // Notify debugger that we are ready to attach to the process if we run a development extension - if (portData) { - if (this._isExtensionDevHost && portData.actual && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { - this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portData.actual); + if (portNumber) { + if (this._isExtensionDevHost && portNumber && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { + this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portNumber); } - this._inspectPort = portData.actual; + this._inspectPort = portNumber; + this._onDidSetInspectPort.fire(); } // Help in case we fail to start it @@ -275,29 +274,31 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { /** * Find a free port if extension host debugging is enabled. */ - private _tryFindDebugPort(): Promise<{ expected: number; actual: number }> { - let expected: number; - let startPort = randomPort(); - if (typeof this._environmentService.debugExtensionHost.port === 'number') { - startPort = expected = this._environmentService.debugExtensionHost.port; + private async _tryFindDebugPort(): Promise { + + if (typeof this._environmentService.debugExtensionHost.port !== 'number') { + return 0; } - return new Promise(resolve => { - return findFreePort(startPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */).then(port => { - if (!port) { - console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color:'); - } else { - if (expected && port !== expected) { - console.warn(`%c[Extension Host] %cProvided debugging port ${expected} is not free, using ${port} instead.`, 'color: blue', 'color:'); - } - if (this._isExtensionDevDebugBrk) { - console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color:'); - } else { - console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color:'); - } - } - return resolve({ expected, actual: port }); - }); - }); + + const expected = this._environmentService.debugExtensionHost.port; + const port = await findFreePort(expected, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */); + + if (!port) { + console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color:'); + return 0; + } + + if (port !== expected) { + console.warn(`%c[Extension Host] %cProvided debugging port ${expected} is not free, using ${port} instead.`, 'color: blue', 'color:'); + } + if (this._isExtensionDevDebugBrk) { + console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color:'); + } else { + console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color:'); + } + return port; + + } private _tryExtHostHandshake(): Promise { @@ -466,6 +467,37 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._onExit.fire([code, signal]); } + public async enableInspectPort(): Promise { + if (typeof this._inspectPort === 'number') { + return true; + } + + if (!this._extensionHostProcess) { + return false; + } + + interface ProcessExt { + _debugProcess?(n: number): any; + } + + if (typeof (process)._debugProcess === 'function') { + // use (undocumented) _debugProcess feature of node + (process)._debugProcess!(this._extensionHostProcess.pid); + await Promise.race([Event.toPromise(this._onDidSetInspectPort.event), timeout(1000)]); + return typeof this._inspectPort === 'number'; + + } else if (!platform.isWindows) { + // use KILL USR1 on non-windows platforms (fallback) + this._extensionHostProcess.kill('SIGUSR1'); + await Promise.race([Event.toPromise(this._onDidSetInspectPort.event), timeout(1000)]); + return typeof this._inspectPort === 'number'; + + } else { + // not supported... + return false; + } + } + public getInspectPort(): number | undefined { return withNullAsUndefined(this._inspectPort); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 9cc156d3d11..6387493212b 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -537,9 +537,9 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); } - public getInspectPort(): number { + public async getInspectPort(tryEnableInspector: boolean): Promise { if (this._extensionHostProcessManagers.length > 0) { - return this._extensionHostProcessManagers[0].getInspectPort(); + return this._extensionHostProcessManagers[0].getInspectPort(tryEnableInspector); } return 0; } diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 9f5a14f6cb0..bbd3b8e3ef5 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -275,20 +275,6 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise { const protocol = await createExtHostProtocol(); diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 4d08f7645a0..17fdf323c7c 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -393,15 +393,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { } private static _isStringArray(arr: string[]): boolean { - if (!Array.isArray(arr)) { - return false; - } - for (let i = 0, len = arr.length; i < len; i++) { - if (typeof arr[i] !== 'string') { - return false; - } - } - return true; + return Array.isArray(arr) && arr.every(value => typeof value === 'string'); } } diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 3b5706ce76a..748960c515e 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -35,9 +35,6 @@ self.postMessage = () => console.trace(`'postMessage' has been blocked`); const nativeAddEventLister = addEventListener.bind(self); self.addEventLister = () => console.trace(`'addEventListener' has been blocked`); -self.indexedDB.open = () => console.trace(`'indexedDB.open' has been blocked`); -self.caches.open = () => console.trace(`'indexedDB.caches' has been blocked`); - //#endregion --- const hostUtil = new class implements IHostUtils { diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index de6fad7d5fd..f0315cfc780 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -120,7 +120,7 @@ export class HistoryService extends Disposable implements IHistoryService { private lastEditLocation: IStackEntry | undefined; - private history!: Array; + private history: Array = []; private recentlyClosedFiles: IRecentlyClosedFile[]; private loaded: boolean; private resourceFilter: ResourceGlobMatcher; @@ -250,7 +250,11 @@ export class HistoryService extends Disposable implements IHistoryService { // Track the last edit location by tracking model content change events // Use a debouncer to make sure to capture the correct cursor position // after the model content has changed. - this.activeEditorListeners.add(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => this.rememberLastEditLocation(activeEditor!, activeTextEditorWidget)))); + this.activeEditorListeners.add(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => { + if (activeEditor) { + this.rememberLastEditLocation(activeEditor, activeTextEditorWidget); + } + }))); } } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index b9b4527ae6e..f68e4e20558 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -66,23 +66,21 @@ export class BrowserHostService extends Disposable implements IHostService { async open() { } }; } - - this.registerListeners(); } - private registerListeners(): void { + private _onDidChangeFocus: Event | undefined; + get onDidChangeFocus(): Event { + if (!this._onDidChangeFocus) { + const focusTracker = this._register(trackFocus(window)); + this._onDidChangeFocus = Event.any( + Event.map(focusTracker.onDidFocus, () => this.hasFocus), + Event.map(focusTracker.onDidBlur, () => this.hasFocus) + ); + } - // Track Focus on Window - const focusTracker = this._register(trackFocus(window)); - this._onDidChangeFocus = Event.any( - Event.map(focusTracker.onDidFocus, () => this.hasFocus), - Event.map(focusTracker.onDidBlur, () => this.hasFocus) - ); + return this._onDidChangeFocus; } - get onDidChangeFocus(): Event { return this._onDidChangeFocus; } - private _onDidChangeFocus: Event; - get hasFocus(): boolean { return document.hasFocus(); } @@ -193,10 +191,6 @@ export class BrowserHostService extends Disposable implements IHostService { async reload(): Promise { window.location.reload(); } - - async closeWorkspace(): Promise { - return this.doOpenEmptyWindow({ forceReuseWindow: true }); - } } registerSingleton(IHostService, BrowserHostService, true); diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index b27f23f0e27..f97c8048de2 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -66,11 +66,5 @@ export interface IHostService { */ reload(): Promise; - /** - * Closes the currently opened folder/workspace and returns to an empty - * window. - */ - closeWorkspace(): Promise; - //#endregion } diff --git a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts index 0d6ff8a2a7a..17d415d16b7 100644 --- a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts +++ b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts @@ -94,10 +94,6 @@ export class DesktopHostService extends Disposable implements IHostService { reload(): Promise { return this.electronService.reload(); } - - closeWorkspace(): Promise { - return this.electronService.closeWorkspace(); - } } registerSingleton(IHostService, DesktopHostService, true); diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index 0b7b04823bd..97a9fcc913b 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -19,7 +19,6 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; @@ -49,6 +48,7 @@ import { FileUserDataProvider } from 'vs/workbench/services/userData/common/file import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; class TestEnvironmentService extends WorkbenchEnvironmentService { @@ -81,11 +81,12 @@ suite('KeybindingsEditing', () => { instantiationService = new TestInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(testDir)); + + const configService = new TestConfigurationService(); + configService.setUserConfiguration('files', { 'eol': '\n' }); + instantiationService.stub(IEnvironmentService, environmentService); - instantiationService.stub(IConfigurationService, ConfigurationService); - instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' }); - instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { }); - instantiationService.stub(IConfigurationService, 'onDidChangeConfiguration', () => { }); + instantiationService.stub(IConfigurationService, configService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); const lifecycleService = new TestLifecycleService(); instantiationService.stub(ILifecycleService, lifecycleService); diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index c17ec465ac6..0176b9ff57e 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -70,7 +70,7 @@ export interface IWorkbenchLayoutService extends ILayoutService { /** * Returns the parts HTML element, if there is one. */ - getContainer(part: Parts): HTMLElement; + getContainer(part: Parts): HTMLElement | undefined; /** * Returns if the part is visible. @@ -80,7 +80,7 @@ export interface IWorkbenchLayoutService extends ILayoutService { /** * Returns if the part is visible. */ - getDimension(part: Parts): Dimension; + getDimension(part: Parts): Dimension | undefined; /** * Set activity bar hidden or not diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 552e4e9fc51..c32c0b9499b 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { INotificationsModel, NotificationsModel, ChoiceAction } from 'vs/workbench/common/notifications'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; @@ -17,15 +17,16 @@ export class NotificationService extends Disposable implements INotificationServ _serviceBrand: undefined; private _model: INotificationsModel = this._register(new NotificationsModel()); - - get model(): INotificationsModel { - return this._model; - } + get model(): INotificationsModel { return this._model; } constructor(@IStorageService private readonly storageService: IStorageService) { super(); } + setFilter(filter: NotificationsFilter): void { + this._model.setFilter(filter); + } + info(message: NotificationMessage | NotificationMessage[]): void { if (Array.isArray(message)) { message.forEach(m => this.info(m)); diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/services/output/common/outputChannelModel.ts index f3a8ec8ad94..2eade61f069 100644 --- a/src/vs/workbench/services/output/common/outputChannelModel.ts +++ b/src/vs/workbench/services/output/common/outputChannelModel.ts @@ -365,7 +365,7 @@ export class BufferredOutputChannel extends Disposable implements IOutputChannel class BufferedContent { - private static MAX_OUTPUT_LENGTH = 10000 /* Max. number of output lines to show in output */ * 100 /* Guestimated chars per line */; + private static readonly MAX_OUTPUT_LENGTH = 10000 /* Max. number of output lines to show in output */ * 100 /* Guestimated chars per line */; private data: string[] = []; private dataIds: number[] = []; diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index d686236e30a..ef626afef88 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -22,18 +22,18 @@ export interface IPanelService { _serviceBrand: undefined; - readonly onDidPanelOpen: Event<{ panel: IPanel, focus: boolean }>; + readonly onDidPanelOpen: Event<{ readonly panel: IPanel, readonly focus: boolean }>; readonly onDidPanelClose: Event; /** * Opens a panel with the given identifier and pass keyboard focus to it if specified. */ - openPanel(id: string, focus?: boolean): IPanel | null; + openPanel(id: string, focus?: boolean): IPanel | undefined; /** * Returns the current active panel or null if none */ - getActivePanel(): IPanel | null; + getActivePanel(): IPanel | undefined; /** * Returns the panel by id. @@ -43,17 +43,17 @@ export interface IPanelService { /** * Returns all built-in panels following the default order */ - getPanels(): IPanelIdentifier[]; + getPanels(): readonly IPanelIdentifier[]; /** * Returns pinned panels following the visual order */ - getPinnedPanels(): IPanelIdentifier[]; + getPinnedPanels(): readonly IPanelIdentifier[]; /** * Returns the progress indicator for the panel bar. */ - getProgressIndicator(id: string): IProgressIndicator | null; + getProgressIndicator(id: string): IProgressIndicator | undefined; /** * Show an activity in a panel. diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 524a952bd02..6da68dca7a6 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -308,7 +308,7 @@ export class ProgressService extends Disposable implements IProgressService { return this.withCompositeProgress(this.panelService.getProgressIndicator(panelid), task, options); } - private withCompositeProgress

, R = unknown>(progressIndicator: IProgressIndicator | null, task: (progress: IProgress) => P, options: IProgressCompositeOptions): P { + private withCompositeProgress

, R = unknown>(progressIndicator: IProgressIndicator | undefined, task: (progress: IProgress) => P, options: IProgressCompositeOptions): P { let progressRunner: IProgressRunner | undefined = undefined; const promise = task({ diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index c889524ee61..93008e7624b 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -21,6 +21,7 @@ import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics import { Emitter } from 'vs/base/common/event'; import { ISignService } from 'vs/platform/sign/common/sign'; import { ILogService } from 'vs/platform/log/common/log'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; export abstract class AbstractRemoteAgentService extends Disposable { @@ -69,6 +70,26 @@ export abstract class AbstractRemoteAgentService extends Disposable { return Promise.resolve(undefined); } + + logTelemetry(eventName: string, data: ITelemetryData): Promise { + const connection = this.getConnection(); + if (connection) { + const client = new RemoteExtensionEnvironmentChannelClient(connection.getChannel('remoteextensionsenvironment')); + return client.logTelemetry(eventName, data); + } + + return Promise.resolve(undefined); + } + + flushTelemetry(): Promise { + const connection = this.getConnection(); + if (connection) { + const client = new RemoteExtensionEnvironmentChannelClient(connection.getChannel('remoteextensionsenvironment')); + return client.flushTelemetry(); + } + + return Promise.resolve(undefined); + } } export class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection { diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index a1877e037aa..93a0c25a574 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -10,6 +10,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { RemoteAuthorities } from 'vs/base/common/network'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; export interface IGetEnvironmentDataArguments { language: string; @@ -70,4 +71,12 @@ export class RemoteExtensionEnvironmentChannelClient { disableTelemetry(): Promise { return this.channel.call('disableTelemetry'); } + + logTelemetry(eventName: string, data: ITelemetryData): Promise { + return this.channel.call('logTelemetry', { eventName, data }); + } + + flushTelemetry(): Promise { + return this.channel.call('flushTelemetry'); + } } diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 1cda189b53e..9200f1be530 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -9,6 +9,7 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { Event } from 'vs/base/common/event'; import { PersistenConnectionEvent as PersistentConnectionEvent, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; export const RemoteExtensionLogFileName = 'remoteagent'; @@ -23,6 +24,8 @@ export interface IRemoteAgentService { getEnvironment(bail?: boolean): Promise; getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise; disableTelemetry(): Promise; + logTelemetry(eventName: string, data?: ITelemetryData): Promise; + flushTelemetry(): Promise; } export interface IRemoteAgentConnection { diff --git a/src/vs/workbench/services/remote/node/tunnelService.ts b/src/vs/workbench/services/remote/node/tunnelService.ts index 32273b70912..62b872ac8c9 100644 --- a/src/vs/workbench/services/remote/node/tunnelService.ts +++ b/src/vs/workbench/services/remote/node/tunnelService.ts @@ -26,7 +26,7 @@ export async function createRemoteTunnel(options: IConnectionOptions, tunnelRemo class NodeRemoteTunnel extends Disposable implements RemoteTunnel { public readonly tunnelRemotePort: number; - public tunnelLocalPort: number; + public tunnelLocalPort!: number; private readonly _options: IConnectionOptions; private readonly _server: net.Server; diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index 554762af58b..d89964a436b 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -425,13 +425,14 @@ function getRgArgs(query: TextSearchQuery, options: TextSearchOptions): string[] args.push('--pcre2'); } - if (query.isRegExp) { - query.pattern = unicodeEscapesToPCRE2(query.pattern); - } - // Allow $ to match /r/n args.push('--crlf'); + if (query.isRegExp) { + query.pattern = unicodeEscapesToPCRE2(query.pattern); + args.push('--auto-hybrid-regex'); + } + let searchPatternAfterDoubleDashes: Maybe; if (query.isWordMatch) { const regexp = createRegExp(query.pattern, !!query.isRegExp, { wholeWord: query.isWordMatch }); @@ -441,7 +442,6 @@ function getRgArgs(query: TextSearchQuery, options: TextSearchOptions): string[] let fixedRegexpQuery = fixRegexNewline(query.pattern); fixedRegexpQuery = fixNewline(fixedRegexpQuery); args.push('--regexp', fixedRegexpQuery); - args.push('--auto-hybrid-regex'); } else { searchPatternAfterDoubleDashes = query.pattern; args.push('--fixed-strings'); diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index a33b25a0e3a..0c62adace7c 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -15,52 +15,24 @@ import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/pla import { IStorageService } from 'vs/platform/storage/common/storage'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/browser/workbenchCommonProperties'; import { IProductService } from 'vs/platform/product/common/productService'; -import { ApplicationInsights } from '@microsoft/applicationinsights-web'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class WebTelemetryAppender implements ITelemetryAppender { - private _aiClient?: ApplicationInsights; - constructor(aiKey: string, private _logService: ILogService) { - const initConfig = { - config: { - instrumentationKey: aiKey, - endpointUrl: 'https://vortex.data.microsoft.com/collect/v1', - emitLineDelimitedJson: true, - autoTrackPageVisitTime: false, - disableExceptionTracking: true, - disableAjaxTracking: true - } - }; - - this._aiClient = new ApplicationInsights(initConfig); - this._aiClient.loadAppInsights(); - } + constructor(private _logService: ILogService, private _appender: IRemoteAgentService) { } log(eventName: string, data: any): void { - if (!this._aiClient) { - return; - } - data = validateTelemetryData(data); this._logService.trace(`telemetry/${eventName}`, data); - this._aiClient.trackEvent({ - name: 'monacoworkbench/' + eventName, + this._appender.logTelemetry('/monacoworkbench/' + eventName, { properties: data.properties, measurements: data.measurements }); } flush(): Promise { - if (this._aiClient) { - return new Promise(resolve => { - this._aiClient!.flush(); - this._aiClient = undefined; - resolve(undefined); - }); - } - - return Promise.resolve(); + return this._appender.flushTelemetry(); } } @@ -75,14 +47,14 @@ export class TelemetryService extends Disposable implements ITelemetryService { @ILogService logService: ILogService, @IConfigurationService configurationService: IConfigurationService, @IStorageService storageService: IStorageService, - @IProductService productService: IProductService + @IProductService productService: IProductService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { super(); - const aiKey = productService.aiConfig && productService.aiConfig.asimovKey; - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry && !!aiKey) { + if (!environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) { const config: ITelemetryServiceConfig = { - appender: combinedAppender(new WebTelemetryAppender(aiKey, logService), new LogAppender(logService)), + appender: combinedAppender(new WebTelemetryAppender(logService, remoteAgentService), new LogAppender(logService)), commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), piiPaths: [environmentService.appRoot] }; diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index a3e41e7f41e..84fac20658e 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -38,7 +38,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { const channel = sharedProcessService.getChannel('telemetryAppender'); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, productService.msftInternalDomains, environmentService.installSourcePath, environmentService.configuration.remoteAuthority, environmentService.options && environmentService.options.resolveCommonTelemetryProperties), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, productService.msftInternalDomains, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), piiPaths: environmentService.extensionsPath ? [environmentService.appRoot, environmentService.extensionsPath] : [environmentService.appRoot] }; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 3181a91d1c1..c6c9a1f1917 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -75,40 +75,34 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private readonly _onDidStateChange: Emitter = this._register(new Emitter()); readonly onDidStateChange: Event = this._onDidStateChange.event; - private resource: URI; + private contentEncoding: string | undefined; // encoding as reported from disk - private contentEncoding: string; // encoding as reported from disk - private preferredEncoding: string; // encoding as chosen by the user + private versionId = 0; + private bufferSavedVersionId: number | undefined; + private blockModelContentChange = false; - private preferredMode: string | undefined; // mode as chosen by the user + private lastResolvedFileStat: IFileStatWithMetadata | undefined; - private versionId: number; - private bufferSavedVersionId: number; - private blockModelContentChange: boolean; - - private lastResolvedFileStat: IFileStatWithMetadata; - - private autoSaveAfterMillies?: number; - private autoSaveAfterMilliesEnabled: boolean; + private autoSaveAfterMillies: number | undefined; + private autoSaveAfterMilliesEnabled: boolean | undefined; private readonly autoSaveDisposable = this._register(new MutableDisposable()); - private saveSequentializer: SaveSequentializer; - private lastSaveAttemptTime: number; + private readonly saveSequentializer = new SaveSequentializer(); + private lastSaveAttemptTime = 0; - private contentChangeEventScheduler: RunOnceScheduler; - private orphanedChangeEventScheduler: RunOnceScheduler; + private readonly contentChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidContentChange.fire(StateChange.CONTENT_CHANGE), TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY)); + private readonly orphanedChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidStateChange.fire(StateChange.ORPHANED_CHANGE), TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY)); - private dirty: boolean; - private inConflictMode: boolean; - private inOrphanMode: boolean; - private inErrorMode: boolean; - - private disposed: boolean; + private dirty = false; + private inConflictMode = false; + private inOrphanMode = false; + private inErrorMode = false; + private disposed = false; constructor( - resource: URI, - preferredEncoding: string, - preferredMode: string | undefined, + private resource: URI, + private preferredEncoding: string | undefined, // encoding as chosen by the user + private preferredMode: string | undefined, // mode as chosen by the user @INotificationService private readonly notificationService: INotificationService, @IModeService modeService: IModeService, @IModelService modelService: IModelService, @@ -123,18 +117,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil ) { super(modelService, modeService); - this.resource = resource; - this.preferredEncoding = preferredEncoding; - this.preferredMode = preferredMode; - this.inOrphanMode = false; - this.dirty = false; - this.versionId = 0; - this.lastSaveAttemptTime = 0; - this.saveSequentializer = new SaveSequentializer(); - - this.contentChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidContentChange.fire(StateChange.CONTENT_CHANGE), TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY)); - this.orphanedChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidStateChange.fire(StateChange.ORPHANED_CHANGE), TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY)); - this.updateAutoSaveConfiguration(textFileService.getAutoSaveConfiguration()); this.registerListeners(); @@ -727,12 +709,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) this.logService.trace(`doSave(${versionId}) - before write()`, this.resource); - return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), { + const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); + return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(lastResolvedFileStat.resource, this.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, overwriteEncoding: options.overwriteEncoding, - mtime: this.lastResolvedFileStat.mtime, + mtime: lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: this.lastResolvedFileStat.etag, + etag: lastResolvedFileStat.etag, writeElevated: options.writeElevated }).then(stat => { this.logService.trace(`doSave(${versionId}) - after write()`, this.resource); @@ -800,11 +783,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return 'keybindings'; } - // Check for locale file - if (isEqual(this.resource, joinPath(this.environmentService.userRoamingDataHome, 'locale.json'))) { - return 'locale'; - } - // Check for snippets if (isEqualOrParent(this.resource, joinPath(this.environmentService.userRoamingDataHome, 'snippets'))) { return 'snippets'; @@ -848,10 +826,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return Promise.resolve(); } - return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), { - mtime: this.lastResolvedFileStat.mtime, + const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); + return this.saveSequentializer.setPending(versionId, this.textFileService.write(lastResolvedFileStat.resource, this.createSnapshot(), { + mtime: lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: this.lastResolvedFileStat.etag + etag: lastResolvedFileStat.etag }).then(stat => { // Updated resolved stat with updated stat since touching it might have changed mtime @@ -951,7 +930,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - getEncoding(): string { + getEncoding(): string | undefined { return this.preferredEncoding || this.contentEncoding; } @@ -992,7 +971,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - updatePreferredEncoding(encoding: string): void { + updatePreferredEncoding(encoding: string | undefined): void { if (!this.isNewEncoding(encoding)) { return; } @@ -1003,7 +982,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this._onDidStateChange.fire(StateChange.ENCODING); } - private isNewEncoding(encoding: string): boolean { + private isNewEncoding(encoding: string | undefined): boolean { if (this.preferredEncoding === encoding) { return false; // return early if the encoding is already the same } @@ -1031,7 +1010,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.resource; } - getStat(): IFileStatWithMetadata { + getStat(): IFileStatWithMetadata | undefined { return this.lastResolvedFileStat; } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 7a87bbb0bd6..e459f336e17 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -38,7 +38,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private readonly _onModelOrphanedChanged: Emitter = this._register(new Emitter()); readonly onModelOrphanedChanged: Event = this._onModelOrphanedChanged.event; - private _onModelsDirty!: Event; + private _onModelsDirty: Event | undefined; get onModelsDirty(): Event { if (!this._onModelsDirty) { this._onModelsDirty = this.debounce(this.onModelDirty); @@ -47,7 +47,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsDirty; } - private _onModelsSaveError!: Event; + private _onModelsSaveError: Event | undefined; get onModelsSaveError(): Event { if (!this._onModelsSaveError) { this._onModelsSaveError = this.debounce(this.onModelSaveError); @@ -56,7 +56,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsSaveError; } - private _onModelsSaved!: Event; + private _onModelsSaved: Event | undefined; get onModelsSaved(): Event { if (!this._onModelsSaved) { this._onModelsSaved = this.debounce(this.onModelSaved); @@ -65,7 +65,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsSaved; } - private _onModelsReverted!: Event; + private _onModelsReverted: Event | undefined; get onModelsReverted(): Event { if (!this._onModelsReverted) { this._onModelsReverted = this.debounce(this.onModelReverted); diff --git a/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts index 95495464e6d..ce6bc5f6eba 100644 --- a/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts @@ -30,9 +30,9 @@ export class TextResourcePropertiesService implements ITextResourcePropertiesSer } getEOL(resource?: URI, language?: string): string { - const filesConfiguration = this.configurationService.getValue<{ eol: string }>('files', { overrideIdentifier: language, resource }); - if (filesConfiguration && filesConfiguration.eol && filesConfiguration.eol !== 'auto') { - return filesConfiguration.eol; + const eol = this.configurationService.getValue('files.eol', { overrideIdentifier: language, resource }); + if (eol && eol !== 'auto') { + return eol; } const os = this.getOS(resource); return os === OperatingSystem.Linux || os === OperatingSystem.Macintosh ? '\n' : '\r\n'; diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 568ad75afca..1293d717f3d 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -463,7 +463,7 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport hasState(state: ModelState): boolean; - updatePreferredEncoding(encoding: string): void; + updatePreferredEncoding(encoding: string | undefined): void; save(options?: ISaveOptions): Promise; diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 6c7b6033307..a7e977a09a2 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -67,7 +67,7 @@ export class NativeTextFileService extends AbstractTextFileService { super(contextService, fileService, untitledEditorService, lifecycleService, instantiationService, configurationService, modeService, modelService, environmentService, notificationService, backupFileService, historyService, contextKeyService, dialogService, fileDialogService, editorService, textResourceConfigurationService); } - private _encoding!: EncodingOracle; + private _encoding: EncodingOracle | undefined; get encoding(): EncodingOracle { if (!this._encoding) { this._encoding = this._register(this.instantiationService.createInstance(EncodingOracle)); diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index 629e19c6b77..07c3271fa95 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -15,6 +15,7 @@ import { FileOperationResult, FileOperationError, IFileService } from 'vs/platfo import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { assertIsDefined } from 'vs/base/common/types'; class ServiceAccessor { constructor(@ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService, @IFileService public fileService: TestFileService) { @@ -286,8 +287,8 @@ suite('Files - TextFileEditorModel', () => { model1.textEditorModel!.setValue('foo'); - const m1Mtime = model1.getStat().mtime; - const m2Mtime = model2.getStat().mtime; + const m1Mtime = assertIsDefined(model1.getStat()).mtime; + const m2Mtime = assertIsDefined(model2.getStat()).mtime; assert.ok(m1Mtime > 0); assert.ok(m2Mtime > 0); @@ -302,8 +303,8 @@ suite('Files - TextFileEditorModel', () => { await accessor.textFileService.saveAll(); assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt'))); - assert.ok(model1.getStat().mtime > m1Mtime); - assert.ok(model2.getStat().mtime > m2Mtime); + assert.ok(assertIsDefined(model1.getStat()).mtime > m1Mtime); + assert.ok(assertIsDefined(model2.getStat()).mtime > m2Mtime); assert.ok(model1.getLastSaveAttemptTime() > m1Mtime); assert.ok(model2.getLastSaveAttemptTime() > m2Mtime); diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 1b5f490f490..857096d4b0d 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -311,4 +311,4 @@ suite('Files - TextFileEditorModelManager', () => { manager.disposeModel((model as TextFileEditorModel)); manager.dispose(); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/browser/fileIconThemeStore.ts index e4eb557f54c..0afad15ee6c 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeStore.ts @@ -14,6 +14,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; +import { find } from 'vs/base/common/arrays'; const iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'iconThemes', @@ -62,7 +63,7 @@ export class FileIconThemeStore extends Disposable { private initialize() { iconThemeExtPoint.setHandler((extensions) => { - const previousIds: { [key: string]: boolean } = {}; + const previousIds: { [key: string]: boolean; } = {}; const added: FileIconThemeData[] = []; for (const theme of this.knownIconThemes) { previousIds[theme.id] = true; @@ -131,12 +132,7 @@ export class FileIconThemeStore extends Disposable { return Promise.resolve(FileIconThemeData.noIconTheme()); } return this.getFileIconThemes().then(allIconSets => { - for (let iconSet of allIconSets) { - if (iconSet.id === iconTheme) { - return iconSet; - } - } - return undefined; + return find(allIconSets, iconSet => iconSet.id === iconTheme); }); } @@ -145,12 +141,7 @@ export class FileIconThemeStore extends Disposable { return Promise.resolve(FileIconThemeData.noIconTheme()); } return this.getFileIconThemes().then(allIconSets => { - for (let iconSet of allIconSets) { - if (iconSet.settingsId === settingsId) { - return iconSet; - } - } - return undefined; + return find(allIconSets, iconSet => iconSet.settingsId === settingsId); }); } diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index b5e65780f81..69483fa673e 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -102,7 +102,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { ) { this.container = layoutService.getWorkbenchContainer(); - this.colorThemeStore = new ColorThemeStore(extensionService, ColorThemeData.createLoadedEmptyTheme(DEFAULT_THEME_ID, DEFAULT_THEME_SETTING_VALUE)); + this.colorThemeStore = new ColorThemeStore(extensionService); this.onFileIconThemeChange = new Emitter(); this.iconThemeStore = new FileIconThemeStore(extensionService); this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); @@ -177,6 +177,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // restore color this.setColorTheme(prevColorId, 'auto'); prevColorId = undefined; + } else { + this.reloadCurrentColorTheme(); } } } @@ -199,6 +201,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { this.setFileIconTheme(prevFileIconId, 'auto'); prevFileIconId = undefined; + } else { + this.reloadCurrentFileIconTheme(); } } } @@ -206,18 +210,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.fileService.onFileChanges(async e => { if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) { - await this.currentColorTheme.reload(this.fileService); - this.currentColorTheme.setCustomColors(this.colorCustomizations); - this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); - this.updateDynamicCSSRules(this.currentColorTheme); - this.applyTheme(this.currentColorTheme, undefined, false); + this.reloadCurrentColorTheme(); } if (this.watchedIconThemeLocation && this.currentIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) { - await this.currentIconTheme.reload(this.fileService); - _applyIconTheme(this.currentIconTheme, () => { - this.doSetFileIconTheme(this.currentIconTheme); - return Promise.resolve(this.currentIconTheme); - }); + this.reloadCurrentFileIconTheme(); } }); } @@ -362,6 +358,14 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } + private async reloadCurrentColorTheme() { + await this.currentColorTheme.reload(this.fileService); + this.currentColorTheme.setCustomColors(this.colorCustomizations); + this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); + this.updateDynamicCSSRules(this.currentColorTheme); + this.applyTheme(this.currentColorTheme, undefined, false); + } + public restoreColorTheme() { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { @@ -499,6 +503,14 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } + private async reloadCurrentFileIconTheme() { + await this.currentIconTheme.reload(this.fileService); + _applyIconTheme(this.currentIconTheme, () => { + this.doSetFileIconTheme(this.currentIconTheme); + return Promise.resolve(this.currentIconTheme); + }); + } + public restoreFileIconTheme() { let fileIconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); if (fileIconThemeSetting !== this.currentIconTheme.settingsId) { diff --git a/src/vs/workbench/services/themes/common/colorThemeStore.ts b/src/vs/workbench/services/themes/common/colorThemeStore.ts index 272338755fc..7253ada3afd 100644 --- a/src/vs/workbench/services/themes/common/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/common/colorThemeStore.ts @@ -57,8 +57,8 @@ export class ColorThemeStore { private readonly onDidChangeEmitter = new Emitter(); public readonly onDidChange: Event = this.onDidChangeEmitter.event; - constructor(@IExtensionService private readonly extensionService: IExtensionService, defaultTheme: ColorThemeData) { - this.extensionsColorThemes = [defaultTheme]; + constructor(@IExtensionService private readonly extensionService: IExtensionService) { + this.extensionsColorThemes = []; this.initialize(); } @@ -69,7 +69,7 @@ export class ColorThemeStore { for (const theme of this.extensionsColorThemes) { previousIds[theme.id] = true; } - this.extensionsColorThemes.length = 1; // remove all but the default theme + this.extensionsColorThemes.length = 0; for (let ext of extensions) { let extensionData = { extensionId: ext.description.identifier.value, @@ -114,11 +114,7 @@ export class ColorThemeStore { } let themeData = ColorThemeData.fromExtensionTheme(theme, colorThemeLocation, extensionData); - if (themeData.id === this.extensionsColorThemes[0].id) { - this.extensionsColorThemes[0] = themeData; - } else { - this.extensionsColorThemes.push(themeData); - } + this.extensionsColorThemes.push(themeData); }); } diff --git a/src/vs/workbench/services/untitled/common/untitledEditorService.ts b/src/vs/workbench/services/untitled/common/untitledEditorService.ts index 8800aded809..2e9781c71c9 100644 --- a/src/vs/workbench/services/untitled/common/untitledEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledEditorService.ts @@ -243,7 +243,7 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } } - const input = this.instantiationService.createInstance(UntitledEditorInput, untitledResource, hasAssociatedFilePath, mode, initialValue, encoding); + const input = this.instantiationService.createInstance(UntitledEditorInput, untitledResource, !!hasAssociatedFilePath, mode, initialValue, encoding); const contentListener = input.onDidModelChangeContent(() => this._onDidChangeContent.fire(untitledResource)); const dirtyListener = input.onDidChangeDirty(() => this._onDidChangeDirty.fire(untitledResource)); diff --git a/src/vs/workbench/services/url/browser/urlService.ts b/src/vs/workbench/services/url/browser/urlService.ts index b073447fc26..eef5070c450 100644 --- a/src/vs/workbench/services/url/browser/urlService.ts +++ b/src/vs/workbench/services/url/browser/urlService.ts @@ -54,7 +54,7 @@ export class BrowserURLService extends AbstractURLService { private registerListeners(): void { if (this.provider) { - this._register(this.provider.onCallback(uri => this.open(uri))); + this._register(this.provider.onCallback(uri => this.open(uri, { trusted: true }))); } } diff --git a/src/vs/workbench/services/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-browser/urlService.ts index 319de3fbd12..73fa15e1f8e 100644 --- a/src/vs/workbench/services/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-browser/urlService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; +import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/common/url'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; @@ -15,6 +15,11 @@ import { IElectronEnvironmentService } from 'vs/workbench/services/electron/elec import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; import { IElectronService } from 'vs/platform/electron/node/electron'; +export interface IRelayOpenURLOptions extends IOpenURLOptions { + openToSide?: boolean; + openExternal?: boolean; +} + export class RelayURLService extends URLService implements IURLHandler { private urlService: IURLService; @@ -46,16 +51,16 @@ export class RelayURLService extends URLService implements IURLHandler { return uri.with({ query }); } - async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { + async open(resource: URI, options?: IRelayOpenURLOptions): Promise { if (resource.scheme !== product.urlProtocol) { return false; } - return await this.urlService.open(resource); + return await this.urlService.open(resource, options); } - async handleURL(uri: URI): Promise { - const result = await super.open(uri); + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { + const result = await super.open(uri, options); if (result) { await this.electronService.focusWindow(); diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index ffc1a81d63a..deebb5b8584 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -23,12 +23,12 @@ export interface IViewletService { /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. */ - openViewlet(id: string | undefined, focus?: boolean): Promise; + openViewlet(id: string | undefined, focus?: boolean): Promise; /** - * Returns the current active viewlet or null if none. + * Returns the current active viewlet if any. */ - getActiveViewlet(): IViewlet | null; + getActiveViewlet(): IViewlet | undefined; /** * Returns the id of the default viewlet. @@ -48,7 +48,7 @@ export interface IViewletService { /** * Returns the progress indicator for the side bar. */ - getProgressIndicator(id: string): IProgressIndicator | null; + getProgressIndicator(id: string): IProgressIndicator | undefined; /** * Hide the active viewlet. diff --git a/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts index 5c983b207b1..bc5c69825bd 100644 --- a/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts @@ -142,6 +142,8 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi return false; } } + + return false; } async isValidTargetWorkspacePath(path: URI): Promise { diff --git a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts index 7442fd4fda7..c26f9a10ecf 100644 --- a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts @@ -29,7 +29,7 @@ suite('Breadcrumb Model', function () { test('only uri, inside workspace', function () { - let model = new EditorBreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, workspaceService, configService); + let model = new EditorBreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, configService, workspaceService); let elements = model.getElements(); assert.equal(elements.length, 3); @@ -44,7 +44,7 @@ suite('Breadcrumb Model', function () { test('only uri, outside workspace', function () { - let model = new EditorBreadcrumbsModel(URI.parse('foo:/outside/file.ts'), undefined, workspaceService, configService); + let model = new EditorBreadcrumbsModel(URI.parse('foo:/outside/file.ts'), undefined, configService, workspaceService); let elements = model.getElements(); assert.equal(elements.length, 2); diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index 652994bc809..46805b2c9da 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -112,7 +112,7 @@ class TestFileEditorInput extends EditorInput implements IFileEditorInput { getTypeId() { return 'testFileEditorInputForGroups'; } resolve(): Promise { return Promise.resolve(null!); } setEncoding(encoding: string) { } - getEncoding(): string { return null!; } + getEncoding() { return undefined; } setPreferredEncoding(encoding: string) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } diff --git a/src/vs/workbench/test/common/notifications.test.ts b/src/vs/workbench/test/common/notifications.test.ts index 4c81c67b61a..5a9dd5781b7 100644 --- a/src/vs/workbench/test/common/notifications.test.ts +++ b/src/vs/workbench/test/common/notifications.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { NotificationsModel, NotificationViewItem, INotificationChangeEvent, NotificationChangeType, NotificationViewItemLabelKind, IStatusMessageChangeEvent, StatusMessageChangeType } from 'vs/workbench/common/notifications'; import { Action } from 'vs/base/common/actions'; -import { INotification, Severity } from 'vs/platform/notification/common/notification'; +import { INotification, Severity, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; suite('Notifications', () => { @@ -127,6 +127,13 @@ suite('Notifications', () => { assert.equal(links[2].title, 'Click to execute command \'without.title\''); assert.equal(links[2].length, '[Link 3](command:without.title)'.length); assert.equal(links[2].offset, 'Unable to [Link 1](http://link1.com) open [Link 2](command:open.me "Open This") and '.length); + + // Filter + let item8 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' }, NotificationsFilter.SILENT)!; + assert.equal(item8.silent, true); + + let item9 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' }, NotificationsFilter.OFF)!; + assert.equal(item9.silent, false); }); test('Model', () => { diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index c01ed376683..9664bc68bd0 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -878,12 +878,12 @@ suite('ExtHostLanguageFeatureCommands', function () { let incoming = await commands.executeCommand('vscode.executeCallHierarchyProviderIncomingCalls', model.uri, new types.Position(0, 10)); assert.equal(incoming.length, 1); - assert.ok(incoming[0].source instanceof types.CallHierarchyItem); - assert.equal(incoming[0].source.name, 'IN'); + assert.ok(incoming[0].from instanceof types.CallHierarchyItem); + assert.equal(incoming[0].from.name, 'IN'); let outgoing = await commands.executeCommand('vscode.executeCallHierarchyProviderOutgoingCalls', model.uri, new types.Position(0, 10)); assert.equal(outgoing.length, 1); - assert.ok(outgoing[0].target instanceof types.CallHierarchyItem); - assert.equal(outgoing[0].target.name, 'OUT'); + assert.ok(outgoing[0].to instanceof types.CallHierarchyItem); + assert.equal(outgoing[0].to.name, 'OUT'); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index c2bfcdd6605..f319881a103 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -20,7 +20,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData suite('ExtHostConfiguration', function () { class RecordingShape extends mock() { - lastArgs: [ConfigurationTarget, string, any]; + lastArgs!: [ConfigurationTarget, string, any]; $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): Promise { this.lastArgs = [target, key, value]; return Promise.resolve(undefined); diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index b3fe2988ec7..64105b9c089 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { MainThreadMessageService } from 'vs/workbench/api/browser/mainThreadMessageService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -55,6 +55,9 @@ const emptyNotificationService = new class implements INotificationService { status(message: string | Error, options?: IStatusMessageOptions): IDisposable { return Disposable.None; } + setFilter(filter: NotificationsFilter): void { + throw new Error('not implemented.'); + } }; class EmptyNotificationService implements INotificationService { @@ -78,11 +81,14 @@ class EmptyNotificationService implements INotificationService { throw new Error('Method not implemented.'); } prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { - throw new Error('not implemented'); + throw new Error('Method not implemented'); } status(message: string, options?: IStatusMessageOptions): IDisposable { return Disposable.None; } + setFilter(filter: NotificationsFilter): void { + throw new Error('Method not implemented.'); + } } suite('ExtHostMessageService', function () { diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index 8dff457b408..bf71ebfaf5c 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -28,7 +28,7 @@ const disposables = new DisposableStore(); let mockMainThreadSearch: MockMainThreadSearch; class MockMainThreadSearch implements MainThreadSearchShape { - lastHandle: number; + lastHandle!: number; results: Array = []; diff --git a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts index c161ca4c462..a2117d7652e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts @@ -18,7 +18,11 @@ suite('ExtHostWebview', () => { const viewType = 'view.type'; const shape = createNoopMainThreadWebviews(); - const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { webviewCspSource: '', webviewResourceRoot: '' }); + const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { + webviewCspSource: '', + webviewResourceRoot: '', + isExtensionDevelopmentDebug: false, + }); let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; @@ -52,7 +56,8 @@ suite('ExtHostWebview', () => { const shape = createNoopMainThreadWebviews(); const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { webviewCspSource: '', - webviewResourceRoot: 'vscode-resource://{{resource}}' + webviewResourceRoot: 'vscode-resource://{{resource}}', + isExtensionDevelopmentDebug: false, }); const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); @@ -92,7 +97,8 @@ suite('ExtHostWebview', () => { const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { webviewCspSource: '', - webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit/{{resource}}` + webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit/{{resource}}`, + isExtensionDevelopmentDebug: false, }); const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts index aa5453e5f6c..483d634e6ea 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts @@ -79,7 +79,7 @@ suite('MainThreadDocumentsAndEditors', () => { onDidPanelOpen = Event.None; onDidPanelClose = Event.None; getActivePanel() { - return null; + return undefined; } }, TestEnvironmentService diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index bd1397d06fb..35fde4c61de 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -111,7 +111,7 @@ suite('MainThreadEditors', () => { onDidPanelOpen = Event.None; onDidPanelClose = Event.None; getActivePanel() { - return null; + return undefined; } }, TestEnvironmentService diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0a726a52ff9..2a50ef134f9 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -87,10 +87,11 @@ import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IElectronService } from 'vs/platform/electron/node/electron'; -import { INativeOpenDialogOptions, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue } from 'vs/platform/dialogs/node/dialogs'; +import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { IBackupMainService, IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup'; import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; +import { find } from 'vs/base/common/arrays'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); @@ -186,11 +187,11 @@ export class TestContextService implements IWorkspaceContextService { } export class TestTextFileService extends NativeTextFileService { - public cleanupBackupsBeforeShutdownCalled: boolean; + public cleanupBackupsBeforeShutdownCalled!: boolean; - private promptPath: URI; - private confirmResult: ConfirmResult; - private resolveTextContentError: FileOperationError | null; + private promptPath!: URI; + private confirmResult!: ConfirmResult; + private resolveTextContentError!: FileOperationError | null; constructor( @IWorkspaceContextService contextService: IWorkspaceContextService, @@ -582,8 +583,8 @@ export class TestViewletService implements IViewletService { onDidViewletOpen = this.onDidViewletOpenEmitter.event; onDidViewletClose = this.onDidViewletCloseEmitter.event; - public openViewlet(id: string, focus?: boolean): Promise { - return Promise.resolve(null!); + public openViewlet(id: string, focus?: boolean): Promise { + return Promise.resolve(undefined); } public getViewlets(): ViewletDescriptor[] { @@ -610,7 +611,7 @@ export class TestViewletService implements IViewletService { } public getProgressIndicator(id: string) { - return null!; + return undefined; } public hideActiveViewlet(): void { } @@ -626,19 +627,19 @@ export class TestPanelService implements IPanelService { onDidPanelOpen = new Emitter<{ panel: IPanel, focus: boolean }>().event; onDidPanelClose = new Emitter().event; - public openPanel(id: string, focus?: boolean): IPanel { - return null!; + public openPanel(id: string, focus?: boolean): undefined { + return undefined; } public getPanel(id: string): any { return activeViewlet; } - public getPanels(): any[] { + public getPanels() { return []; } - public getPinnedPanels(): any[] { + public getPinnedPanels() { return []; } @@ -700,14 +701,8 @@ export class TestEditorGroupsService implements IEditorGroupsService { return this.groups; } - getGroup(identifier: number): IEditorGroup { - for (const group of this.groups) { - if (group.id === identifier) { - return group; - } - } - - return undefined!; + getGroup(identifier: number): IEditorGroup | undefined { + return find(this.groups, group => group.id === identifier); } getLabel(_identifier: number): string { @@ -762,7 +757,7 @@ export class TestEditorGroupsService implements IEditorGroupsService { return false; } - partOptions: IEditorPartOptions; + partOptions!: IEditorPartOptions; enforcePartOptions(options: IEditorPartOptions): IDisposable { return Disposable.None; } @@ -773,20 +768,20 @@ export class TestEditorGroup implements IEditorGroupView { constructor(public id: number) { } get group(): EditorGroup { throw new Error('not implemented'); } - activeControl: IVisibleEditor; - activeEditor: IEditorInput; - previewEditor: IEditorInput; - count: number; - disposed: boolean; + activeControl!: IVisibleEditor; + activeEditor!: IEditorInput; + previewEditor!: IEditorInput; + count!: number; + disposed!: boolean; editors: ReadonlyArray = []; - label: string; - index: number; + label!: string; + index!: number; whenRestored: Promise = Promise.resolve(undefined); - element: HTMLElement; - minimumWidth: number; - maximumWidth: number; - minimumHeight: number; - maximumHeight: number; + element!: HTMLElement; + minimumWidth!: number; + maximumWidth!: number; + minimumHeight!: number; + maximumHeight!: number; isEmpty = true; isMinimized = false; @@ -877,9 +872,9 @@ export class TestEditorService implements EditorServiceImpl { onDidCloseEditor: Event = Event.None; onDidOpenEditorFail: Event = Event.None; - activeControl: IVisibleEditor; + activeControl!: IVisibleEditor; activeTextEditorWidget: any; - activeEditor: IEditorInput; + activeEditor!: IEditorInput; editors: ReadonlyArray = []; visibleControls: ReadonlyArray = []; visibleTextEditorWidgets = []; @@ -929,7 +924,7 @@ export class TestFileService implements IFileService { readonly onError: Event = Event.None; private content = 'Hello Html'; - private lastReadFileUri: URI; + private lastReadFileUri!: URI; constructor() { this._onFileChanges = new Emitter(); @@ -1184,8 +1179,8 @@ export class TestLifecycleService implements ILifecycleService { public _serviceBrand: undefined; - public phase: LifecyclePhase; - public startupKind: StartupKind; + public phase!: LifecyclePhase; + public startupKind!: StartupKind; private readonly _onBeforeShutdown = new Emitter(); private readonly _onWillShutdown = new Emitter(); @@ -1246,12 +1241,10 @@ export class TestTextResourcePropertiesService implements ITextResourcePropertie ) { } - getEOL(resource: URI): string { - const filesConfiguration = this.configurationService.getValue<{ eol: string }>('files'); - if (filesConfiguration && filesConfiguration.eol) { - if (filesConfiguration.eol !== 'auto') { - return filesConfiguration.eol; - } + getEOL(resource: URI, language?: string): string { + const eol = this.configurationService.getValue('files.eol', { overrideIdentifier: language, resource }); + if (eol && eol !== 'auto') { + return eol; } return (isLinux || isMacintosh) ? '\n' : '\r\n'; } @@ -1317,7 +1310,6 @@ export class TestHostService implements IHostService { async restart(): Promise { } async reload(): Promise { } - async closeWorkspace(): Promise { } async focus(): Promise { } @@ -1355,9 +1347,9 @@ export class TestElectronService implements IElectronService { async minimizeWindow(): Promise { } async isWindowFocused(): Promise { return true; } async focusWindow(options?: { windowId?: number | undefined; } | undefined): Promise { } - async showMessageBox(options: Electron.MessageBoxOptions): Promise { throw new Error('Method not implemented.'); } - async showSaveDialog(options: Electron.SaveDialogOptions): Promise { throw new Error('Method not implemented.'); } - async showOpenDialog(options: Electron.OpenDialogOptions): Promise { throw new Error('Method not implemented.'); } + async showMessageBox(options: Electron.MessageBoxOptions): Promise { throw new Error('Method not implemented.'); } + async showSaveDialog(options: Electron.SaveDialogOptions): Promise { throw new Error('Method not implemented.'); } + async showOpenDialog(options: Electron.OpenDialogOptions): Promise { throw new Error('Method not implemented.'); } async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise { } async pickFileAndOpen(options: INativeOpenDialogOptions): Promise { } async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise { } @@ -1375,7 +1367,6 @@ export class TestElectronService implements IElectronService { async toggleWindowTabsBar(): Promise { } async relaunch(options?: { addArgs?: string[] | undefined; removeArgs?: string[] | undefined; } | undefined): Promise { } async reload(): Promise { } - async closeWorkspace(): Promise { } async closeWindow(): Promise { } async quit(): Promise { } async openDevTools(options?: Electron.OpenDevToolsOptions | undefined): Promise { } @@ -1448,15 +1439,15 @@ export class TestDialogMainService implements IDialogMainService { throw new Error('Method not implemented.'); } - showMessageBox(options: Electron.MessageBoxOptions, window?: Electron.BrowserWindow | undefined): Promise { + showMessageBox(options: Electron.MessageBoxOptions, window?: Electron.BrowserWindow | undefined): Promise { throw new Error('Method not implemented.'); } - showSaveDialog(options: Electron.SaveDialogOptions, window?: Electron.BrowserWindow | undefined): Promise { + showSaveDialog(options: Electron.SaveDialogOptions, window?: Electron.BrowserWindow | undefined): Promise { throw new Error('Method not implemented.'); } - showOpenDialog(options: Electron.OpenDialogOptions, window?: Electron.BrowserWindow | undefined): Promise { + showOpenDialog(options: Electron.OpenDialogOptions, window?: Electron.BrowserWindow | undefined): Promise { throw new Error('Method not implemented.'); } } diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 3dd3700dc0d..18980940dfb 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -62,6 +62,7 @@ import 'vs/workbench/services/update/electron-browser/updateService'; import 'vs/workbench/services/issue/electron-browser/issueService'; import 'vs/workbench/services/menubar/electron-browser/menubarService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; @@ -111,9 +112,6 @@ import 'vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution // Execution import 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; -// Update -import 'vs/workbench/contrib/update/electron-browser/update.contribution'; - // Performance import 'vs/workbench/contrib/performance/electron-browser/performance.contribution'; @@ -132,11 +130,10 @@ import 'vs/workbench/contrib/tasks/electron-browser/taskService'; // User Data Sync import 'vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution'; -// Welcome -import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; +// Telemetry Opt Out +import 'vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution'; // Configuration Exporter import 'vs/workbench/contrib/configExporter/node/configurationExportHelper.contribution'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 4833155c493..0140a8cec27 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -82,6 +82,7 @@ interface IWorkbenchConstructionOptions { */ logLevel?: LogLevel; + /** * Experimental: Support for update reporting. */ @@ -150,5 +151,5 @@ export { // Updates IUpdateProvider, - IUpdate + IUpdate, }; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index f5fe40a4cae..2dce5dc5939 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -96,7 +96,7 @@ import 'vs/workbench/contrib/debug/browser/extensionHostDebugService'; // Webview import 'vs/workbench/contrib/webview/browser/webviewService'; -import 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; // Terminal import 'vs/workbench/contrib/terminal/browser/terminalNativeService'; @@ -105,7 +105,7 @@ import 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; // Tasks import 'vs/workbench/contrib/tasks/browser/taskService'; -// Welcome -import 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution'; +// Telemetry Opt Out +import 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution'; //#endregion diff --git a/test/automation/src/keybindings.ts b/test/automation/src/keybindings.ts index 70be12b2378..0e8739dd04c 100644 --- a/test/automation/src/keybindings.ts +++ b/test/automation/src/keybindings.ts @@ -24,7 +24,7 @@ export class KeybindingsEditor { await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item'); await this.code.waitForElement('.keybindings-list-container .monaco-list-row.keybinding-item.focused.selected'); - await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item .action-item .codicon.add'); + await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item .action-item .codicon.codicon-add'); await this.code.waitForActiveElement('.defineKeybindingWidget .monaco-inputbox input'); await this.code.dispatchKeybinding(keybinding); diff --git a/test/automation/src/puppeteerDriver.ts b/test/automation/src/puppeteerDriver.ts index ceb76add39c..40fd699b6d3 100644 --- a/test/automation/src/puppeteerDriver.ts +++ b/test/automation/src/puppeteerDriver.ts @@ -92,9 +92,17 @@ let endpoint: string | undefined; export async function launch(_args: string[]): Promise { args = _args; - const webUserDataDir = args.filter(e => e.includes('--user-data-dir='))[0].replace('--user-data-dir=', ''); - await promisify(mkdir)(webUserDataDir); - server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--browser', 'none', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); + const agentFolder = args.filter(e => e.includes('--user-data-dir='))[0].replace('--user-data-dir=', ''); + await promisify(mkdir)(agentFolder); + const env = { + VSCODE_AGENT_FOLDER: agentFolder, + ...process.env + }; + server = spawn( + join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), + ['--browser', 'none', '--driver', 'web'], + { env } + ); server.stderr.on('data', e => console.log('Server stderr: ' + e)); server.stdout.on('data', e => console.log('Server stdout: ' + e)); process.on('exit', teardown); diff --git a/test/automation/src/scm.ts b/test/automation/src/scm.ts index 519faddb27c..60a33abb93f 100644 --- a/test/automation/src/scm.ts +++ b/test/automation/src/scm.ts @@ -9,7 +9,7 @@ import { findElement, findElements, Code } from './code'; const VIEWLET = 'div[id="workbench.view.scm"]'; const SCM_INPUT = `${VIEWLET} .scm-editor textarea`; -const SCM_RESOURCE = `${VIEWLET} .monaco-list-row > .resource`; +const SCM_RESOURCE = `${VIEWLET} .monaco-list-row .resource`; const REFRESH_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[title="Refresh"]`; const COMMIT_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[title="Commit"]`; const SCM_RESOURCE_CLICK = (name: string) => `${SCM_RESOURCE} .monaco-icon-label[title*="${name}"] .label-name`; @@ -67,7 +67,7 @@ export class SCM extends Viewlet { async unstage(name: string): Promise { await this.code.waitAndClick(SCM_RESOURCE_ACTION_CLICK(name, 'Unstage Changes')); - await this.waitForChange('app.js', 'Modified'); + await this.waitForChange(name, 'Modified'); } async commit(message: string): Promise { diff --git a/test/automation/src/search.ts b/test/automation/src/search.ts index 2b8076cc27e..8f7767802f5 100644 --- a/test/automation/src/search.ts +++ b/test/automation/src/search.ts @@ -84,11 +84,11 @@ export class Search extends Viewlet { } async expandReplace(): Promise { - await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.collapse`); + await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.codicon-chevron-right`); } async collapseReplace(): Promise { - await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.expand`); + await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.codicon-chevron-down`); } async setReplaceText(text: string): Promise { @@ -100,12 +100,12 @@ export class Search extends Viewlet { await retry( () => this.code.waitAndClick(fileMatch), - () => this.code.waitForElement(`${fileMatch} .action-label.codicon.action-replace-all`, el => !!el && el.top > 0 && el.left > 0, 10) + () => this.code.waitForElement(`${fileMatch} .action-label.codicon.codicon-replace-all`, el => !!el && el.top > 0 && el.left > 0, 10) ); // ¯\_(ツ)_/¯ await new Promise(c => setTimeout(c, 500)); - await this.code.waitAndClick(`${fileMatch} .action-label.codicon.action-replace-all`); + await this.code.waitAndClick(`${fileMatch} .action-label.codicon.codicon-replace-all`); } async waitForResultText(text: string): Promise { diff --git a/test/electron/index.js b/test/electron/index.js index f21b3615c0e..386515dc414 100644 --- a/test/electron/index.js +++ b/test/electron/index.js @@ -9,7 +9,7 @@ const { join } = require('path'); const path = require('path'); const mocha = require('mocha'); const events = require('events'); -const MochaJUnitReporter = require('mocha-junit-reporter'); +// const MochaJUnitReporter = require('mocha-junit-reporter'); const url = require('url'); const defaultReporterName = process.platform === 'win32' ? 'list' : 'spec'; @@ -133,12 +133,13 @@ app.on('ready', () => { if (argv.tfs) { new mocha.reporters.Spec(runner); - new MochaJUnitReporter(runner, { - reporterOptions: { - testsuitesTitle: `${argv.tfs} ${process.platform}`, - mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined - } - }); + // TODO@deepak the mocha Junit reporter seems to cause a hang when running with Electron 6 inside docker container + // new MochaJUnitReporter(runner, { + // reporterOptions: { + // testsuitesTitle: `${argv.tfs} ${process.platform}`, + // mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined + // } + // }); } else { const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); let Reporter; diff --git a/test/smoke/README.md b/test/smoke/README.md index dc905211950..5f86605f15a 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -43,7 +43,12 @@ yarn smoketest --build PATH_TO_NEW_RELEASE_PARENT_FOLDER --stable-build PATH_TO_ ### Develop -Start a watch task in `test/smoke`: +Start two watch tasks: + +```bash +cd test/automation +yarn watch +``` ```bash cd test/smoke diff --git a/test/smoke/package.json b/test/smoke/package.json index be3067a8727..aedec3f8717 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -19,7 +19,7 @@ "cpx": "^1.5.0", "htmlparser2": "^3.9.2", "mkdirp": "^0.5.1", - "mocha": "^5.2.0", + "mocha": "^6.1.4", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "ncp": "^2.0.0", diff --git a/test/smoke/src/areas/git/git.test.ts b/test/smoke/src/areas/git/git.test.ts index 300c832694d..91948d04b1f 100644 --- a/test/smoke/src/areas/git/git.test.ts +++ b/test/smoke/src/areas/git/git.test.ts @@ -51,6 +51,7 @@ export function setup() { await app.workbench.scm.waitForChange('app.js', 'Modified'); await app.workbench.scm.stage('app.js'); + await app.workbench.scm.openChange('app.js'); await app.workbench.scm.unstage('app.js'); }); @@ -60,6 +61,7 @@ export function setup() { await app.workbench.scm.openSCMViewlet(); await app.workbench.scm.waitForChange('app.js', 'Modified'); + await app.workbench.scm.openChange('app.js'); await app.workbench.scm.stage('app.js'); await app.workbench.scm.commit('first commit'); diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index 221596fc2cb..330b02367e8 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -62,6 +62,11 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + ansi-regex@^0.2.0, ansi-regex@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" @@ -77,11 +82,23 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-styles@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" integrity sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94= +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" @@ -103,6 +120,13 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -254,6 +278,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + chalk@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" @@ -265,6 +294,15 @@ chalk@0.5.1: strip-ansi "^0.3.0" supports-color "^0.2.0" +chalk@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" @@ -301,6 +339,15 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -314,10 +361,17 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= commander@2.6.0: version "2.6.0" @@ -400,12 +454,12 @@ date-fns@^1.23.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" integrity sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw== -debug@3.1.0, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== +debug@3.2.6, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: - ms "2.0.0" + ms "^2.1.1" debug@^2.2.0, debug@^2.3.3: version "2.6.9" @@ -414,12 +468,17 @@ debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: - ms "^2.1.1" + ms "2.0.0" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: version "0.2.0" @@ -431,6 +490,13 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -506,16 +572,51 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.0: +es-abstract@^1.5.1: + version "1.14.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" + integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.0" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.0.0" + string.prototype.trimright "^2.0.0" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + exec-sh@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.1.tgz#163b98a6e89e6b65b47c2a28d215bc1f63989c38" @@ -617,6 +718,20 @@ find-index@^0.1.1: resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" integrity sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ= +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -656,6 +771,11 @@ fsevents@^1.0.0: nan "^2.12.1" node-pre-gyp "^0.12.0" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -670,6 +790,11 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -697,7 +822,19 @@ glob2base@^0.0.12: dependencies: find-index "^0.1.1" -glob@7.1.2, glob@^7.0.5: +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.5: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== @@ -736,6 +873,11 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -772,10 +914,17 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== htmlparser2@^3.9.2: version "3.9.2" @@ -847,6 +996,16 @@ is-buffer@^1.1.5, is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-buffer@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -861,6 +1020,11 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -963,6 +1127,20 @@ is-primitive@^2.0.0: resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -973,6 +1151,11 @@ isarray@1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -985,6 +1168,14 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -1014,16 +1205,36 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash@^4.16.4: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== +lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + lodash@^4.5.1: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" integrity sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw== +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -1159,29 +1370,41 @@ mocha-multi-reporters@^1.1.7: debug "^3.1.0" lodash "^4.16.4" -mocha@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== +mocha@^6.1.4: + version "6.2.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.1.tgz#da941c99437da9bac412097859ff99543969f94c" + integrity sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A== dependencies: + ansi-colors "3.2.3" browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" + debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" - glob "7.1.2" + find-up "3.0.0" + glob "7.1.3" growl "1.10.5" - he "1.1.1" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" minimatch "3.0.4" mkdirp "0.5.1" - supports-color "5.4.0" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.0" + yargs-parser "13.1.1" + yargs-unparser "1.6.0" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.1, ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== @@ -1222,6 +1445,14 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + node-pre-gyp@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" @@ -1295,6 +1526,16 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-inspect@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -1302,6 +1543,24 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -1342,6 +1601,25 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-limit@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" + integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -1357,6 +1635,11 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1485,6 +1768,16 @@ repeat-string@^1.5.2, repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -1546,7 +1839,12 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== -set-blocking@~2.0.0: +semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -1639,6 +1937,11 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -1664,6 +1967,31 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string.prototype.trimleft@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" @@ -1699,7 +2027,14 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-json-comments@2.0.1, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -1711,10 +2046,10 @@ subarg@^1.0.0: dependencies: minimist "^1.1.0" -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== dependencies: has-flag "^3.0.0" @@ -1730,6 +2065,13 @@ supports-color@^3.2.3: dependencies: has-flag "^1.0.0" +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + tar@^4: version "4.4.10" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" @@ -1826,13 +2168,34 @@ watch@^1.0.2: exec-sh "^0.2.0" minimist "^1.2.0" -wide-align@^1.1.0: +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3, wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -1843,7 +2206,45 @@ xml@^1.0.0: resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + yallist@^3.0.0, yallist@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + +yargs-parser@13.1.1, yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.0, yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" diff --git a/tslint.json b/tslint.json index 31adda1904b..a0feadaf2c0 100644 --- a/tslint.json +++ b/tslint.json @@ -472,8 +472,7 @@ "**/vs/workbench/api/{common,browser}/**", "**/vs/workbench/services/**/{common,browser}/**", "vscode-textmate", - "onigasm-umd", - "@microsoft/applicationinsights-web" + "onigasm-umd" ] }, { diff --git a/yarn.lock b/yarn.lock index a234c0630cb..8b48f5f6231 100644 --- a/yarn.lock +++ b/yarn.lock @@ -95,69 +95,6 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" -"@microsoft/applicationinsights-analytics-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.1.1.tgz#6d09c1915f808026e2d45165d04802f09affed59" - integrity sha512-VKIutoFKY99CyKwxLUuj6Vnq14/QwXo9/QSQDpYnHEjo+uKn7QmLsHqWw0K9uYNfNAXt4BZimX/zDg6jZtzeXg== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-channel-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.1.1.tgz#e205eddd93e49d17d9e0711a612b4bfc9810888f" - integrity sha512-fYr9IAqtaEr9AmaPaL3SLQVT3t3GQzl+n74gpNKyAVakDIm0nYQ/bimjdcAhJMDf1VGNSPg/xICneyuZg7Wxlg== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-common@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.1.1.tgz#27e6074584a7a3a8ca3f11f7ff2b7ff0f395bf2d" - integrity sha512-2hkS1Ia1FmAjCuYZ5JlG20/WgObqdsKtmK5YALAFGHIB4KSQ/Za1qazS+7GsG+E0F9UJivNWL1geUIcNqg5Qjg== - dependencies: - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-core-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.1.1.tgz#30fb6a519cc1c6119c419c4811ce72c260217d9e" - integrity sha512-4t4wf6SKqIcWEQDPg/uOhm+BxtHhu/AFreyEoYZmMfcxzAu33h1FtTQRtxBNbYH1+thiNZCh80yUpnT7d9Hrlw== - dependencies: - tslib "^1.9.3" - -"@microsoft/applicationinsights-dependencies-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.1.1.tgz#8154c3efcb24617d015d0bce7c2cc47797a8d3c4" - integrity sha512-yhb4EToBp+aI+qLo0h5NDNtoo3sDFV60uyIOK843YjzXqVotcXX/lRShlghTkJtYH09QhrdzDjViUHnD4sMFSQ== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-properties-js@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.1.1.tgz#ca34232766eb16167b5d87693e2ae5d94f2a1559" - integrity sha512-8l+/ppw6xKTam2RL4EHZ52Lcf217olw81j6kyBNKtIcGwSnLNHrFwEeF3vBWIteG2JKzlg1GhGjrkB3oxXsV2g== - dependencies: - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - tslib "^1.9.3" - -"@microsoft/applicationinsights-web@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.1.1.tgz#1a44eddda7c244b88d9eb052dab6c855682e4f05" - integrity sha512-crvhCkNsNxkFuPWmttyWNSAA96D5FxBtKS6UA9MV9f9XHevTfchf/E3AuU9JZcsXufWMQLwLrUQ9ZiA1QJ0EWA== - dependencies: - "@microsoft/applicationinsights-analytics-js" "2.1.1" - "@microsoft/applicationinsights-channel-js" "2.1.1" - "@microsoft/applicationinsights-common" "2.1.1" - "@microsoft/applicationinsights-core-js" "2.1.1" - "@microsoft/applicationinsights-dependencies-js" "2.1.1" - "@microsoft/applicationinsights-properties-js" "2.1.1" - "@types/commander@^2.11.0": version "2.12.2" resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae" @@ -1502,6 +1439,11 @@ chromium-pickle-js@^0.2.0: resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= +ci-info@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -3020,6 +2962,11 @@ find-cache-dir@^1.0.0: make-dir "^1.0.0" pkg-dir "^2.0.0" +find-parent-dir@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" + integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ= + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -3568,7 +3515,12 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" -graceful-fs@4.1.11, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" + integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== + +graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= @@ -3782,10 +3734,10 @@ gulp-symdest@^1.1.1: queue "^3.1.0" vinyl-fs "^2.4.3" -gulp-tsb@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.4.tgz#c0486534e2f86cd4a11c2393593d9eae3a426ac4" - integrity sha512-BIIls2PpT3+JR1Svvd0SLjkBj2AmlHIRnum/ajf1XQUgbVA9wwsZuTjpVXU/K06KbTYfGsSSYAPRK2dICNAMZQ== +gulp-tsb@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.5.tgz#2c4b41c5049de374934ca0fdb90dd713638af17a" + integrity sha512-zzjLZZBByrtljBhTbDBsScMVB10TYMXiMUV6FUKogs/hpHyTWdkqtMW/5nDPfsDA1xA+HFLgB19FAgWuPE0ZYw== dependencies: ansi-colors "^1.0.1" fancy-log "^1.3.2" @@ -4097,6 +4049,16 @@ https-proxy-agent@2.2.1, https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" +husky@^0.13.1: + version "0.13.4" + resolved "https://registry.yarnpkg.com/husky/-/husky-0.13.4.tgz#48785c5028de3452a51c48c12c4f94b2124a1407" + integrity sha1-SHhcUCjeNFKlHEjBLE+UshJKFAc= + dependencies: + chalk "^1.1.3" + find-parent-dir "^0.3.0" + is-ci "^1.0.9" + normalize-path "^1.0.0" + iconv-lite@0.4.19, iconv-lite@^0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -4339,6 +4301,13 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-ci@^1.0.9: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== + dependencies: + ci-info "^1.5.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -5802,6 +5771,11 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" + integrity sha1-MtDkcvkf80VwHBWoMRAY07CpA3k= + normalize-path@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" @@ -8452,11 +8426,6 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@^1.9.3: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - tslint@^5.16.0: version "5.16.0" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.16.0.tgz#ae61f9c5a98d295b9a4f4553b1b1e831c1984d67" @@ -9302,20 +9271,20 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0.tgz#46659c7c33f9fc268ad3e7e6c5824bb2fdb65852" - integrity sha512-C/v2VvFn3hb1qUgjJPo7LxzxNCLBgNJv8n6v/bH2NqPz32/PNUF+IHu0SFf1TaIH+pydUpKXCtob5a/UyZg/+Q== +xterm-addon-search@0.3.0-beta5: + version "0.3.0-beta5" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta5.tgz#fd53d33a77a0235018479c712be8c12f7c0d083a" + integrity sha512-3GkGc4hST35/4hzgnQPLLvQ29WH7MkZ0mUrBE/Vm1IQum7TnMvWPTkGemwM+wAl4tdBmynNccHJlFeQzaQtVUg== xterm-addon-web-links@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.0.tgz#b408a0be46211d8d4a0bb5e701d8f3c2bd07d473" integrity sha512-dq81c4Pzli2PgKVBgY2REte9sCVibR3df8AP3SEvCTM9uYFnUFxtxzMTplPnc7+rXabVhFdbU6x+rstIk8HNQg== -xterm@4.1.0-beta8: - version "4.1.0-beta8" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.1.0-beta8.tgz#c1ef323ba336d92f5b52302b66f672dfff75b3ef" - integrity sha512-6lf+XVv0qT285w49P92tSYoUB406jdbgdhnPKNzxCIGtGX8kcwK+pHZ8HncDwcEhmTmI4LZ/WXPGtOQJg+onwg== +xterm@4.2.0-beta4: + version "4.2.0-beta4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta4.tgz#596577f94a1da372119d192363ea2b19d1f8b50c" + integrity sha512-BmkpxCpqdOJoNdIcddkRT8S65sGjgBbWI0uIJNSnzZvj81OKcraMSTmF/ODw0TF/MDLc33Fx9cpDx/D6lQgl8Q== y18n@^3.2.1: version "3.2.1"